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