]> git.lizzy.rs Git - minetest.git/blob - src/mapblock.cpp
Disable mesh cache by default
[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
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_gamedef(gamedef),
73                 m_modified(MOD_STATE_WRITE_NEEDED),
74                 m_modified_reason(MOD_REASON_INITIAL),
75                 is_underground(false),
76                 m_lighting_expired(true),
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                 //JMutexAutoLock 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_CLIENT_VER_LOWEST, "Serialize 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_lighting_expired)
575                 flags |= 0x04;
576         if(m_generated == false)
577                 flags |= 0x08;
578         writeU8(os, flags);
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);
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, u16 net_proto_version)
643 {
644         if(data == NULL)
645         {
646                 throw SerializationError("ERROR: Not writing dummy block.");
647         }
648
649         if(net_proto_version >= 21){
650                 int version = 1;
651                 writeU8(os, version);
652                 writeF1000(os, 0); // deprecated heat
653                 writeF1000(os, 0); // deprecated humidity
654         }
655 }
656
657 void MapBlock::deSerialize(std::istream &is, u8 version, bool disk)
658 {
659         if(!ser_ver_supported(version))
660                 throw VersionMismatchException("ERROR: MapBlock format not supported");
661
662         TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())<<std::endl);
663
664         m_day_night_differs_expired = false;
665
666         if(version <= 21)
667         {
668                 deSerialize_pre22(is, version, disk);
669                 return;
670         }
671
672         u8 flags = readU8(is);
673         is_underground = (flags & 0x01) ? true : false;
674         m_day_night_differs = (flags & 0x02) ? true : false;
675         m_lighting_expired = (flags & 0x04) ? true : false;
676         m_generated = (flags & 0x08) ? false : true;
677
678         /*
679                 Bulk node data
680         */
681         TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
682                         <<": Bulk node data"<<std::endl);
683         u8 content_width = readU8(is);
684         u8 params_width = readU8(is);
685         if(content_width != 1 && content_width != 2)
686                 throw SerializationError("MapBlock::deSerialize(): invalid content_width");
687         if(params_width != 2)
688                 throw SerializationError("MapBlock::deSerialize(): invalid params_width");
689         MapNode::deSerializeBulk(is, version, data, nodecount,
690                         content_width, params_width, true);
691
692         /*
693                 NodeMetadata
694         */
695         TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
696                         <<": Node metadata"<<std::endl);
697         // Ignore errors
698         try{
699                 std::ostringstream oss(std::ios_base::binary);
700                 decompressZlib(is, oss);
701                 std::istringstream iss(oss.str(), std::ios_base::binary);
702                 if(version >= 23)
703                         m_node_metadata.deSerialize(iss, m_gamedef);
704                 else
705                         content_nodemeta_deserialize_legacy(iss,
706                                         &m_node_metadata, &m_node_timers,
707                                         m_gamedef);
708         }
709         catch(SerializationError &e)
710         {
711                 errorstream<<"WARNING: MapBlock::deSerialize(): Ignoring an error"
712                                 <<" while deserializing node metadata at ("
713                                 <<PP(getPos())<<": "<<e.what()<<std::endl;
714         }
715
716         /*
717                 Data that is only on disk
718         */
719         if(disk)
720         {
721                 // Node timers
722                 if(version == 23){
723                         // Read unused zero
724                         readU8(is);
725                 }
726                 if(version == 24){
727                         TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
728                                         <<": Node timers (ver==24)"<<std::endl);
729                         m_node_timers.deSerialize(is, version);
730                 }
731
732                 // Static objects
733                 TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
734                                 <<": Static objects"<<std::endl);
735                 m_static_objects.deSerialize(is);
736
737                 // Timestamp
738                 TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
739                                 <<": Timestamp"<<std::endl);
740                 setTimestamp(readU32(is));
741                 m_disk_timestamp = m_timestamp;
742
743                 // Dynamically re-set ids based on node names
744                 TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
745                                 <<": NameIdMapping"<<std::endl);
746                 NameIdMapping nimap;
747                 nimap.deSerialize(is);
748                 correctBlockNodeIds(&nimap, data, m_gamedef);
749
750                 if(version >= 25){
751                         TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
752                                         <<": Node timers (ver>=25)"<<std::endl);
753                         m_node_timers.deSerialize(is, version);
754                 }
755         }
756
757         TRACESTREAM(<<"MapBlock::deSerialize "<<PP(getPos())
758                         <<": Done."<<std::endl);
759 }
760
761 void MapBlock::deSerializeNetworkSpecific(std::istream &is)
762 {
763         try {
764                 int version = readU8(is);
765                 //if(version != 1)
766                 //      throw SerializationError("unsupported MapBlock version");
767                 if(version >= 1) {
768                         readF1000(is); // deprecated heat
769                         readF1000(is); // deprecated humidity
770                 }
771         }
772         catch(SerializationError &e)
773         {
774                 errorstream<<"WARNING: MapBlock::deSerializeNetworkSpecific(): Ignoring an error"
775                                 <<": "<<e.what()<<std::endl;
776         }
777 }
778
779 /*
780         Legacy serialization
781 */
782
783 void MapBlock::deSerialize_pre22(std::istream &is, u8 version, bool disk)
784 {
785         // Initialize default flags
786         is_underground = false;
787         m_day_night_differs = false;
788         m_lighting_expired = false;
789         m_generated = true;
790
791         // Make a temporary buffer
792         u32 ser_length = MapNode::serializedLength(version);
793         SharedBuffer<u8> databuf_nodelist(nodecount * ser_length);
794
795         // These have no compression
796         if(version <= 3 || version == 5 || version == 6)
797         {
798                 char tmp;
799                 is.read(&tmp, 1);
800                 if(is.gcount() != 1)
801                         throw SerializationError
802                                         ("MapBlock::deSerialize: no enough input data");
803                 is_underground = tmp;
804                 is.read((char*)*databuf_nodelist, nodecount * ser_length);
805                 if((u32)is.gcount() != nodecount * ser_length)
806                         throw SerializationError
807                                         ("MapBlock::deSerialize: no enough input data");
808         }
809         else if(version <= 10)
810         {
811                 u8 t8;
812                 is.read((char*)&t8, 1);
813                 is_underground = t8;
814
815                 {
816                         // Uncompress and set material data
817                         std::ostringstream os(std::ios_base::binary);
818                         decompress(is, os, version);
819                         std::string s = os.str();
820                         if(s.size() != nodecount)
821                                 throw SerializationError
822                                                 ("MapBlock::deSerialize: invalid format");
823                         for(u32 i=0; i<s.size(); i++)
824                         {
825                                 databuf_nodelist[i*ser_length] = s[i];
826                         }
827                 }
828                 {
829                         // Uncompress and set param data
830                         std::ostringstream os(std::ios_base::binary);
831                         decompress(is, os, version);
832                         std::string s = os.str();
833                         if(s.size() != nodecount)
834                                 throw SerializationError
835                                                 ("MapBlock::deSerialize: invalid format");
836                         for(u32 i=0; i<s.size(); i++)
837                         {
838                                 databuf_nodelist[i*ser_length + 1] = s[i];
839                         }
840                 }
841
842                 if(version >= 10)
843                 {
844                         // Uncompress and set param2 data
845                         std::ostringstream os(std::ios_base::binary);
846                         decompress(is, os, version);
847                         std::string s = os.str();
848                         if(s.size() != nodecount)
849                                 throw SerializationError
850                                                 ("MapBlock::deSerialize: invalid format");
851                         for(u32 i=0; i<s.size(); i++)
852                         {
853                                 databuf_nodelist[i*ser_length + 2] = s[i];
854                         }
855                 }
856         }
857         // All other versions (newest)
858         else
859         {
860                 u8 flags;
861                 is.read((char*)&flags, 1);
862                 is_underground = (flags & 0x01) ? true : false;
863                 m_day_night_differs = (flags & 0x02) ? true : false;
864                 m_lighting_expired = (flags & 0x04) ? true : false;
865                 if(version >= 18)
866                         m_generated = (flags & 0x08) ? false : true;
867
868                 // Uncompress data
869                 std::ostringstream os(std::ios_base::binary);
870                 decompress(is, os, version);
871                 std::string s = os.str();
872                 if(s.size() != nodecount*3)
873                         throw SerializationError
874                                         ("MapBlock::deSerialize: decompress resulted in size"
875                                         " other than nodecount*3");
876
877                 // deserialize nodes from buffer
878                 for(u32 i=0; i<nodecount; i++)
879                 {
880                         databuf_nodelist[i*ser_length] = s[i];
881                         databuf_nodelist[i*ser_length + 1] = s[i+nodecount];
882                         databuf_nodelist[i*ser_length + 2] = s[i+nodecount*2];
883                 }
884
885                 /*
886                         NodeMetadata
887                 */
888                 if(version >= 14)
889                 {
890                         // Ignore errors
891                         try{
892                                 if(version <= 15)
893                                 {
894                                         std::string data = deSerializeString(is);
895                                         std::istringstream iss(data, std::ios_base::binary);
896                                         content_nodemeta_deserialize_legacy(iss,
897                                                         &m_node_metadata, &m_node_timers,
898                                                         m_gamedef);
899                                 }
900                                 else
901                                 {
902                                         //std::string data = deSerializeLongString(is);
903                                         std::ostringstream oss(std::ios_base::binary);
904                                         decompressZlib(is, oss);
905                                         std::istringstream iss(oss.str(), std::ios_base::binary);
906                                         content_nodemeta_deserialize_legacy(iss,
907                                                         &m_node_metadata, &m_node_timers,
908                                                         m_gamedef);
909                                 }
910                         }
911                         catch(SerializationError &e)
912                         {
913                                 errorstream<<"WARNING: MapBlock::deSerialize(): Ignoring an error"
914                                                 <<" while deserializing node metadata"<<std::endl;
915                         }
916                 }
917         }
918
919         // Deserialize node data
920         for(u32 i=0; i<nodecount; i++)
921         {
922                 data[i].deSerialize(&databuf_nodelist[i*ser_length], version);
923         }
924
925         if(disk)
926         {
927                 /*
928                         Versions up from 9 have block objects. (DEPRECATED)
929                 */
930                 if(version >= 9){
931                         u16 count = readU16(is);
932                         // Not supported and length not known if count is not 0
933                         if(count != 0){
934                                 errorstream<<"WARNING: MapBlock::deSerialize_pre22(): "
935                                                 <<"Ignoring stuff coming at and after MBOs"<<std::endl;
936                                 return;
937                         }
938                 }
939
940                 /*
941                         Versions up from 15 have static objects.
942                 */
943                 if(version >= 15)
944                         m_static_objects.deSerialize(is);
945
946                 // Timestamp
947                 if(version >= 17){
948                         setTimestamp(readU32(is));
949                         m_disk_timestamp = m_timestamp;
950                 } else {
951                         setTimestamp(BLOCK_TIMESTAMP_UNDEFINED);
952                 }
953
954                 // Dynamically re-set ids based on node names
955                 NameIdMapping nimap;
956                 // If supported, read node definition id mapping
957                 if(version >= 21){
958                         nimap.deSerialize(is);
959                 // Else set the legacy mapping
960                 } else {
961                         content_mapnode_get_name_id_mapping(&nimap);
962                 }
963                 correctBlockNodeIds(&nimap, data, m_gamedef);
964         }
965
966
967         // Legacy data changes
968         // This code has to convert from pre-22 to post-22 format.
969         INodeDefManager *nodedef = m_gamedef->ndef();
970         for(u32 i=0; i<nodecount; i++)
971         {
972                 const ContentFeatures &f = nodedef->get(data[i].getContent());
973                 // Mineral
974                 if(nodedef->getId("default:stone") == data[i].getContent()
975                                 && data[i].getParam1() == 1)
976                 {
977                         data[i].setContent(nodedef->getId("default:stone_with_coal"));
978                         data[i].setParam1(0);
979                 }
980                 else if(nodedef->getId("default:stone") == data[i].getContent()
981                                 && data[i].getParam1() == 2)
982                 {
983                         data[i].setContent(nodedef->getId("default:stone_with_iron"));
984                         data[i].setParam1(0);
985                 }
986                 // facedir_simple
987                 if(f.legacy_facedir_simple)
988                 {
989                         data[i].setParam2(data[i].getParam1());
990                         data[i].setParam1(0);
991                 }
992                 // wall_mounted
993                 if(f.legacy_wallmounted)
994                 {
995                         u8 wallmounted_new_to_old[8] = {0x04, 0x08, 0x01, 0x02, 0x10, 0x20, 0, 0};
996                         u8 dir_old_format = data[i].getParam2();
997                         u8 dir_new_format = 0;
998                         for(u8 j=0; j<8; j++)
999                         {
1000                                 if((dir_old_format & wallmounted_new_to_old[j]) != 0)
1001                                 {
1002                                         dir_new_format = j;
1003                                         break;
1004                                 }
1005                         }
1006                         data[i].setParam2(dir_new_format);
1007                 }
1008         }
1009
1010 }
1011
1012 /*
1013         Get a quick string to describe what a block actually contains
1014 */
1015 std::string analyze_block(MapBlock *block)
1016 {
1017         if(block == NULL)
1018                 return "NULL";
1019
1020         std::ostringstream desc;
1021
1022         v3s16 p = block->getPos();
1023         char spos[20];
1024         snprintf(spos, 20, "(%2d,%2d,%2d), ", p.X, p.Y, p.Z);
1025         desc<<spos;
1026
1027         switch(block->getModified())
1028         {
1029         case MOD_STATE_CLEAN:
1030                 desc<<"CLEAN,           ";
1031                 break;
1032         case MOD_STATE_WRITE_AT_UNLOAD:
1033                 desc<<"WRITE_AT_UNLOAD, ";
1034                 break;
1035         case MOD_STATE_WRITE_NEEDED:
1036                 desc<<"WRITE_NEEDED,    ";
1037                 break;
1038         default:
1039                 desc<<"unknown getModified()="+itos(block->getModified())+", ";
1040         }
1041
1042         if(block->isGenerated())
1043                 desc<<"is_gen [X], ";
1044         else
1045                 desc<<"is_gen [ ], ";
1046
1047         if(block->getIsUnderground())
1048                 desc<<"is_ug [X], ";
1049         else
1050                 desc<<"is_ug [ ], ";
1051
1052         if(block->getLightingExpired())
1053                 desc<<"lighting_exp [X], ";
1054         else
1055                 desc<<"lighting_exp [ ], ";
1056
1057         if(block->isDummy())
1058         {
1059                 desc<<"Dummy, ";
1060         }
1061         else
1062         {
1063                 bool full_ignore = true;
1064                 bool some_ignore = false;
1065                 bool full_air = true;
1066                 bool some_air = false;
1067                 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
1068                 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
1069                 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
1070                 {
1071                         v3s16 p(x0,y0,z0);
1072                         MapNode n = block->getNodeNoEx(p);
1073                         content_t c = n.getContent();
1074                         if(c == CONTENT_IGNORE)
1075                                 some_ignore = true;
1076                         else
1077                                 full_ignore = false;
1078                         if(c == CONTENT_AIR)
1079                                 some_air = true;
1080                         else
1081                                 full_air = false;
1082                 }
1083
1084                 desc<<"content {";
1085
1086                 std::ostringstream ss;
1087
1088                 if(full_ignore)
1089                         ss<<"IGNORE (full), ";
1090                 else if(some_ignore)
1091                         ss<<"IGNORE, ";
1092
1093                 if(full_air)
1094                         ss<<"AIR (full), ";
1095                 else if(some_air)
1096                         ss<<"AIR, ";
1097
1098                 if(ss.str().size()>=2)
1099                         desc<<ss.str().substr(0, ss.str().size()-2);
1100
1101                 desc<<"}, ";
1102         }
1103
1104         return desc.str().substr(0, desc.str().size()-2);
1105 }
1106
1107
1108 //END