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