Move tool stuff to tool.{h,cpp}
[oweals/minetest.git] / src / mapblock.cpp
1 /*
2 Minetest-c55
3 Copyright (C) 2010 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 General Public License as published by
7 the Free Software Foundation; either version 2 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 General Public License for more details.
14
15 You should have received a copy of the GNU 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 #include "map.h"
22 // For g_settings
23 #include "main.h"
24 #include "light.h"
25 #include <sstream>
26 #include "mapnode_contentfeatures.h"
27 #include "nodemetadata.h"
28
29 /*
30         MapBlock
31 */
32
33 MapBlock::MapBlock(Map *parent, v3s16 pos, bool dummy):
34                 m_node_metadata(new NodeMetadataList),
35                 m_parent(parent),
36                 m_pos(pos),
37                 m_modified(MOD_STATE_WRITE_NEEDED),
38                 is_underground(false),
39                 m_lighting_expired(true),
40                 m_day_night_differs(false),
41                 m_generated(false),
42                 m_timestamp(BLOCK_TIMESTAMP_UNDEFINED),
43                 m_usage_timer(0)
44 {
45         data = NULL;
46         if(dummy == false)
47                 reallocate();
48         
49 #ifndef SERVER
50         m_mesh_expired = false;
51         mesh_mutex.Init();
52         mesh = NULL;
53         m_temp_mods_mutex.Init();
54 #endif
55 }
56
57 MapBlock::~MapBlock()
58 {
59 #ifndef SERVER
60         {
61                 JMutexAutoLock lock(mesh_mutex);
62                 
63                 if(mesh)
64                 {
65                         mesh->drop();
66                         mesh = NULL;
67                 }
68         }
69 #endif
70
71         delete m_node_metadata;
72
73         if(data)
74                 delete[] data;
75 }
76
77 bool MapBlock::isValidPositionParent(v3s16 p)
78 {
79         if(isValidPosition(p))
80         {
81                 return true;
82         }
83         else{
84                 return m_parent->isValidPosition(getPosRelative() + p);
85         }
86 }
87
88 MapNode MapBlock::getNodeParent(v3s16 p)
89 {
90         if(isValidPosition(p) == false)
91         {
92                 return m_parent->getNode(getPosRelative() + p);
93         }
94         else
95         {
96                 if(data == NULL)
97                         throw InvalidPositionException();
98                 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
99         }
100 }
101
102 void MapBlock::setNodeParent(v3s16 p, MapNode & n)
103 {
104         if(isValidPosition(p) == false)
105         {
106                 m_parent->setNode(getPosRelative() + p, n);
107         }
108         else
109         {
110                 if(data == NULL)
111                         throw InvalidPositionException();
112                 data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X] = n;
113         }
114 }
115
116 MapNode MapBlock::getNodeParentNoEx(v3s16 p)
117 {
118         if(isValidPosition(p) == false)
119         {
120                 try{
121                         return m_parent->getNode(getPosRelative() + p);
122                 }
123                 catch(InvalidPositionException &e)
124                 {
125                         return MapNode(CONTENT_IGNORE);
126                 }
127         }
128         else
129         {
130                 if(data == NULL)
131                 {
132                         return MapNode(CONTENT_IGNORE);
133                 }
134                 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
135         }
136 }
137
138 #ifndef SERVER
139
140 #if 1
141 void MapBlock::updateMesh(u32 daynight_ratio)
142 {
143 #if 0
144         /*
145                 DEBUG: If mesh has been generated, don't generate it again
146         */
147         {
148                 JMutexAutoLock meshlock(mesh_mutex);
149                 if(mesh != NULL)
150                         return;
151         }
152 #endif
153
154         MeshMakeData data;
155         data.fill(daynight_ratio, this);
156         
157         scene::SMesh *mesh_new = makeMapBlockMesh(&data);
158         
159         /*
160                 Replace the mesh
161         */
162
163         replaceMesh(mesh_new);
164
165 }
166 #endif
167
168 void MapBlock::replaceMesh(scene::SMesh *mesh_new)
169 {
170         mesh_mutex.Lock();
171
172         //scene::SMesh *mesh_old = mesh[daynight_i];
173         //mesh[daynight_i] = mesh_new;
174
175         scene::SMesh *mesh_old = mesh;
176         mesh = mesh_new;
177         setMeshExpired(false);
178         
179         if(mesh_old != NULL)
180         {
181                 // Remove hardware buffers of meshbuffers of mesh
182                 // NOTE: No way, this runs in a different thread and everything
183                 /*u32 c = mesh_old->getMeshBufferCount();
184                 for(u32 i=0; i<c; i++)
185                 {
186                         IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
187                 }*/
188                 
189                 /*dstream<<"mesh_old->getReferenceCount()="
190                                 <<mesh_old->getReferenceCount()<<std::endl;
191                 u32 c = mesh_old->getMeshBufferCount();
192                 for(u32 i=0; i<c; i++)
193                 {
194                         scene::IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
195                         dstream<<"buf->getReferenceCount()="
196                                         <<buf->getReferenceCount()<<std::endl;
197                 }*/
198
199                 // Drop the mesh
200                 mesh_old->drop();
201
202                 //delete mesh_old;
203         }
204
205         mesh_mutex.Unlock();
206 }
207         
208 #endif // !SERVER
209
210 /*
211         Propagates sunlight down through the block.
212         Doesn't modify nodes that are not affected by sunlight.
213         
214         Returns false if sunlight at bottom block is invalid.
215         Returns true if sunlight at bottom block is valid.
216         Returns true if bottom block doesn't exist.
217
218         If there is a block above, continues from it.
219         If there is no block above, assumes there is sunlight, unless
220         is_underground is set or highest node is water.
221
222         All sunlighted nodes are added to light_sources.
223
224         if remove_light==true, sets non-sunlighted nodes black.
225
226         if black_air_left!=NULL, it is set to true if non-sunlighted
227         air is left in block.
228 */
229 bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources,
230                 bool remove_light, bool *black_air_left)
231 {
232         // Whether the sunlight at the top of the bottom block is valid
233         bool block_below_is_valid = true;
234         
235         v3s16 pos_relative = getPosRelative();
236         
237         for(s16 x=0; x<MAP_BLOCKSIZE; x++)
238         {
239                 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
240                 {
241 #if 1
242                         bool no_sunlight = false;
243                         bool no_top_block = false;
244                         // Check if node above block has sunlight
245                         try{
246                                 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
247                                 if(n.getContent() == CONTENT_IGNORE)
248                                 {
249                                         // Trust heuristics
250                                         no_sunlight = is_underground;
251                                 }
252                                 else if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
253                                 {
254                                         no_sunlight = true;
255                                 }
256                         }
257                         catch(InvalidPositionException &e)
258                         {
259                                 no_top_block = true;
260                                 
261                                 // NOTE: This makes over-ground roofed places sunlighted
262                                 // Assume sunlight, unless is_underground==true
263                                 if(is_underground)
264                                 {
265                                         no_sunlight = true;
266                                 }
267                                 else
268                                 {
269                                         MapNode n = getNode(v3s16(x, MAP_BLOCKSIZE-1, z));
270                                         //if(n.getContent() == CONTENT_WATER || n.getContent() == CONTENT_WATERSOURCE)
271                                         if(content_features(n).sunlight_propagates == false)
272                                         {
273                                                 no_sunlight = true;
274                                         }
275                                 }
276                                 // NOTE: As of now, this just would make everything dark.
277                                 // No sunlight here
278                                 //no_sunlight = true;
279                         }
280 #endif
281 #if 0 // Doesn't work; nothing gets light.
282                         bool no_sunlight = true;
283                         bool no_top_block = false;
284                         // Check if node above block has sunlight
285                         try{
286                                 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
287                                 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
288                                 {
289                                         no_sunlight = false;
290                                 }
291                         }
292                         catch(InvalidPositionException &e)
293                         {
294                                 no_top_block = true;
295                         }
296 #endif
297
298                         /*std::cout<<"("<<x<<","<<z<<"): "
299                                         <<"no_top_block="<<no_top_block
300                                         <<", is_underground="<<is_underground
301                                         <<", no_sunlight="<<no_sunlight
302                                         <<std::endl;*/
303                 
304                         s16 y = MAP_BLOCKSIZE-1;
305                         
306                         // This makes difference to diminishing in water.
307                         bool stopped_to_solid_object = false;
308                         
309                         u8 current_light = no_sunlight ? 0 : LIGHT_SUN;
310
311                         for(; y >= 0; y--)
312                         {
313                                 v3s16 pos(x, y, z);
314                                 MapNode &n = getNodeRef(pos);
315                                 
316                                 if(current_light == 0)
317                                 {
318                                         // Do nothing
319                                 }
320                                 else if(current_light == LIGHT_SUN && n.sunlight_propagates())
321                                 {
322                                         // Do nothing: Sunlight is continued
323                                 }
324                                 else if(n.light_propagates() == false)
325                                 {
326                                         /*// DEPRECATED TODO: REMOVE
327                                         if(grow_grass)
328                                         {
329                                                 bool upper_is_air = false;
330                                                 try
331                                                 {
332                                                         if(getNodeParent(pos+v3s16(0,1,0)).getContent() == CONTENT_AIR)
333                                                                 upper_is_air = true;
334                                                 }
335                                                 catch(InvalidPositionException &e)
336                                                 {
337                                                 }
338                                                 // Turn mud into grass
339                                                 if(upper_is_air && n.getContent() == CONTENT_MUD
340                                                                 && current_light == LIGHT_SUN)
341                                                 {
342                                                         n.d = CONTENT_GRASS;
343                                                 }
344                                         }*/
345
346                                         // A solid object is on the way.
347                                         stopped_to_solid_object = true;
348                                         
349                                         // Light stops.
350                                         current_light = 0;
351                                 }
352                                 else
353                                 {
354                                         // Diminish light
355                                         current_light = diminish_light(current_light);
356                                 }
357
358                                 u8 old_light = n.getLight(LIGHTBANK_DAY);
359
360                                 if(current_light > old_light || remove_light)
361                                 {
362                                         n.setLight(LIGHTBANK_DAY, current_light);
363                                 }
364                                 
365                                 if(diminish_light(current_light) != 0)
366                                 {
367                                         light_sources.insert(pos_relative + pos, true);
368                                 }
369
370                                 if(current_light == 0 && stopped_to_solid_object)
371                                 {
372                                         if(black_air_left)
373                                         {
374                                                 *black_air_left = true;
375                                         }
376                                 }
377                         }
378
379                         // Whether or not the block below should see LIGHT_SUN
380                         bool sunlight_should_go_down = (current_light == LIGHT_SUN);
381
382                         /*
383                                 If the block below hasn't already been marked invalid:
384
385                                 Check if the node below the block has proper sunlight at top.
386                                 If not, the block below is invalid.
387                                 
388                                 Ignore non-transparent nodes as they always have no light
389                         */
390                         try
391                         {
392                         if(block_below_is_valid)
393                         {
394                                 MapNode n = getNodeParent(v3s16(x, -1, z));
395                                 if(n.light_propagates())
396                                 {
397                                         if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN
398                                                         && sunlight_should_go_down == false)
399                                                 block_below_is_valid = false;
400                                         else if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN
401                                                         && sunlight_should_go_down == true)
402                                                 block_below_is_valid = false;
403                                 }
404                         }//if
405                         }//try
406                         catch(InvalidPositionException &e)
407                         {
408                                 /*std::cout<<"InvalidBlockException for bottom block node"
409                                                 <<std::endl;*/
410                                 // Just no block below, no need to panic.
411                         }
412                 }
413         }
414
415         return block_below_is_valid;
416 }
417
418
419 void MapBlock::copyTo(VoxelManipulator &dst)
420 {
421         v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
422         VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
423         
424         // Copy from data to VoxelManipulator
425         dst.copyFrom(data, data_area, v3s16(0,0,0),
426                         getPosRelative(), data_size);
427 }
428
429 void MapBlock::copyFrom(VoxelManipulator &dst)
430 {
431         v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
432         VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
433         
434         // Copy from VoxelManipulator to data
435         dst.copyTo(data, data_area, v3s16(0,0,0),
436                         getPosRelative(), data_size);
437 }
438
439 void MapBlock::updateDayNightDiff()
440 {
441         if(data == NULL)
442         {
443                 m_day_night_differs = false;
444                 return;
445         }
446
447         bool differs = false;
448
449         /*
450                 Check if any lighting value differs
451         */
452         for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
453         {
454                 MapNode &n = data[i];
455                 if(n.getLight(LIGHTBANK_DAY) != n.getLight(LIGHTBANK_NIGHT))
456                 {
457                         differs = true;
458                         break;
459                 }
460         }
461
462         /*
463                 If some lighting values differ, check if the whole thing is
464                 just air. If it is, differ = false
465         */
466         if(differs)
467         {
468                 bool only_air = true;
469                 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
470                 {
471                         MapNode &n = data[i];
472                         if(n.getContent() != CONTENT_AIR)
473                         {
474                                 only_air = false;
475                                 break;
476                         }
477                 }
478                 if(only_air)
479                         differs = false;
480         }
481
482         // Set member variable
483         m_day_night_differs = differs;
484 }
485
486 s16 MapBlock::getGroundLevel(v2s16 p2d)
487 {
488         if(isDummy())
489                 return -3;
490         try
491         {
492                 s16 y = MAP_BLOCKSIZE-1;
493                 for(; y>=0; y--)
494                 {
495                         MapNode n = getNodeRef(p2d.X, y, p2d.Y);
496                         if(content_features(n).walkable)
497                         {
498                                 if(y == MAP_BLOCKSIZE-1)
499                                         return -2;
500                                 else
501                                         return y;
502                         }
503                 }
504                 return -1;
505         }
506         catch(InvalidPositionException &e)
507         {
508                 return -3;
509         }
510 }
511
512 /*
513         Serialization
514 */
515
516 void MapBlock::serialize(std::ostream &os, u8 version)
517 {
518         if(!ser_ver_supported(version))
519                 throw VersionMismatchException("ERROR: MapBlock format not supported");
520         
521         if(data == NULL)
522         {
523                 throw SerializationError("ERROR: Not writing dummy block.");
524         }
525         
526         // These have no compression
527         if(version <= 3 || version == 5 || version == 6)
528         {
529                 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
530                 
531                 u32 buflen = 1 + nodecount * MapNode::serializedLength(version);
532                 SharedBuffer<u8> dest(buflen);
533
534                 dest[0] = is_underground;
535                 for(u32 i=0; i<nodecount; i++)
536                 {
537                         u32 s = 1 + i * MapNode::serializedLength(version);
538                         data[i].serialize(&dest[s], version);
539                 }
540                 
541                 os.write((char*)*dest, dest.getSize());
542         }
543         else if(version <= 10)
544         {
545                 /*
546                         With compression.
547                         Compress the materials and the params separately.
548                 */
549                 
550                 // First byte
551                 os.write((char*)&is_underground, 1);
552
553                 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
554
555                 // Get and compress materials
556                 SharedBuffer<u8> materialdata(nodecount);
557                 for(u32 i=0; i<nodecount; i++)
558                 {
559                         materialdata[i] = data[i].param0;
560                 }
561                 compress(materialdata, os, version);
562
563                 // Get and compress lights
564                 SharedBuffer<u8> lightdata(nodecount);
565                 for(u32 i=0; i<nodecount; i++)
566                 {
567                         lightdata[i] = data[i].param1;
568                 }
569                 compress(lightdata, os, version);
570                 
571                 if(version >= 10)
572                 {
573                         // Get and compress param2
574                         SharedBuffer<u8> param2data(nodecount);
575                         for(u32 i=0; i<nodecount; i++)
576                         {
577                                 param2data[i] = data[i].param2;
578                         }
579                         compress(param2data, os, version);
580                 }
581         }
582         // All other versions (newest)
583         else
584         {
585                 // First byte
586                 u8 flags = 0;
587                 if(is_underground)
588                         flags |= 0x01;
589                 if(m_day_night_differs)
590                         flags |= 0x02;
591                 if(m_lighting_expired)
592                         flags |= 0x04;
593                 if(version >= 18)
594                 {
595                         if(m_generated == false)
596                                 flags |= 0x08;
597                 }
598                 os.write((char*)&flags, 1);
599
600                 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
601
602                 /*
603                         Get data
604                 */
605
606                 // Serialize nodes
607                 SharedBuffer<u8> databuf_nodelist(nodecount*3);
608                 for(u32 i=0; i<nodecount; i++)
609                 {
610                         data[i].serialize(&databuf_nodelist[i*3], version);
611                 }
612                 
613                 // Create buffer with different parameters sorted
614                 SharedBuffer<u8> databuf(nodecount*3);
615                 for(u32 i=0; i<nodecount; i++)
616                 {
617                         databuf[i] = databuf_nodelist[i*3];
618                         databuf[i+nodecount] = databuf_nodelist[i*3+1];
619                         databuf[i+nodecount*2] = databuf_nodelist[i*3+2];
620                 }
621
622                 /*
623                         Compress data to output stream
624                 */
625
626                 compress(databuf, os, version);
627                 
628                 /*
629                         NodeMetadata
630                 */
631                 if(version >= 14)
632                 {
633                         if(version <= 15)
634                         {
635                                 try{
636                                         std::ostringstream oss(std::ios_base::binary);
637                                         m_node_metadata->serialize(oss);
638                                         os<<serializeString(oss.str());
639                                 }
640                                 // This will happen if the string is longer than 65535
641                                 catch(SerializationError &e)
642                                 {
643                                         // Use an empty string
644                                         os<<serializeString("");
645                                 }
646                         }
647                         else
648                         {
649                                 std::ostringstream oss(std::ios_base::binary);
650                                 m_node_metadata->serialize(oss);
651                                 compressZlib(oss.str(), os);
652                                 //os<<serializeLongString(oss.str());
653                         }
654                 }
655         }
656 }
657
658 void MapBlock::deSerialize(std::istream &is, u8 version)
659 {
660         if(!ser_ver_supported(version))
661                 throw VersionMismatchException("ERROR: MapBlock format not supported");
662
663         // These have no lighting info
664         if(version <= 1)
665         {
666                 setLightingExpired(true);
667         }
668
669         // These have no "generated" field
670         if(version < 18)
671         {
672                 m_generated = true;
673         }
674
675         // These have no compression
676         if(version <= 3 || version == 5 || version == 6)
677         {
678                 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
679                 char tmp;
680                 is.read(&tmp, 1);
681                 if(is.gcount() != 1)
682                         throw SerializationError
683                                         ("MapBlock::deSerialize: no enough input data");
684                 is_underground = tmp;
685                 for(u32 i=0; i<nodecount; i++)
686                 {
687                         s32 len = MapNode::serializedLength(version);
688                         SharedBuffer<u8> d(len);
689                         is.read((char*)*d, len);
690                         if(is.gcount() != len)
691                                 throw SerializationError
692                                                 ("MapBlock::deSerialize: no enough input data");
693                         data[i].deSerialize(*d, version);
694                 }
695         }
696         else if(version <= 10)
697         {
698                 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
699
700                 u8 t8;
701                 is.read((char*)&t8, 1);
702                 is_underground = t8;
703
704                 {
705                         // Uncompress and set material data
706                         std::ostringstream os(std::ios_base::binary);
707                         decompress(is, os, version);
708                         std::string s = os.str();
709                         if(s.size() != nodecount)
710                                 throw SerializationError
711                                                 ("MapBlock::deSerialize: invalid format");
712                         for(u32 i=0; i<s.size(); i++)
713                         {
714                                 data[i].param0 = s[i];
715                         }
716                 }
717                 {
718                         // Uncompress and set param data
719                         std::ostringstream os(std::ios_base::binary);
720                         decompress(is, os, version);
721                         std::string s = os.str();
722                         if(s.size() != nodecount)
723                                 throw SerializationError
724                                                 ("MapBlock::deSerialize: invalid format");
725                         for(u32 i=0; i<s.size(); i++)
726                         {
727                                 data[i].param1 = s[i];
728                         }
729                 }
730         
731                 if(version >= 10)
732                 {
733                         // Uncompress and set param2 data
734                         std::ostringstream os(std::ios_base::binary);
735                         decompress(is, os, version);
736                         std::string s = os.str();
737                         if(s.size() != nodecount)
738                                 throw SerializationError
739                                                 ("MapBlock::deSerialize: invalid format");
740                         for(u32 i=0; i<s.size(); i++)
741                         {
742                                 data[i].param2 = s[i];
743                         }
744                 }
745         }
746         // All other versions (newest)
747         else
748         {
749                 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
750
751                 u8 flags;
752                 is.read((char*)&flags, 1);
753                 is_underground = (flags & 0x01) ? true : false;
754                 m_day_night_differs = (flags & 0x02) ? true : false;
755                 m_lighting_expired = (flags & 0x04) ? true : false;
756                 if(version >= 18)
757                         m_generated = (flags & 0x08) ? false : true;
758
759                 // Uncompress data
760                 std::ostringstream os(std::ios_base::binary);
761                 decompress(is, os, version);
762                 std::string s = os.str();
763                 if(s.size() != nodecount*3)
764                         throw SerializationError
765                                         ("MapBlock::deSerialize: decompress resulted in size"
766                                         " other than nodecount*3");
767
768                 // deserialize nodes from buffer
769                 for(u32 i=0; i<nodecount; i++)
770                 {
771                         u8 buf[3];
772                         buf[0] = s[i];
773                         buf[1] = s[i+nodecount];
774                         buf[2] = s[i+nodecount*2];
775                         data[i].deSerialize(buf, version);
776                 }
777                 
778                 /*
779                         NodeMetadata
780                 */
781                 if(version >= 14)
782                 {
783                         // Ignore errors
784                         try{
785                                 if(version <= 15)
786                                 {
787                                         std::string data = deSerializeString(is);
788                                         std::istringstream iss(data, std::ios_base::binary);
789                                         m_node_metadata->deSerialize(iss);
790                                 }
791                                 else
792                                 {
793                                         //std::string data = deSerializeLongString(is);
794                                         std::ostringstream oss(std::ios_base::binary);
795                                         decompressZlib(is, oss);
796                                         std::istringstream iss(oss.str(), std::ios_base::binary);
797                                         m_node_metadata->deSerialize(iss);
798                                 }
799                         }
800                         catch(SerializationError &e)
801                         {
802                                 dstream<<"WARNING: MapBlock::deSerialize(): Ignoring an error"
803                                                 <<" while deserializing node metadata"<<std::endl;
804                         }
805                 }
806         }
807 }
808
809 void MapBlock::serializeDiskExtra(std::ostream &os, u8 version)
810 {
811         // Versions up from 9 have block objects. (DEPRECATED)
812         if(version >= 9)
813         {
814                 // count=0
815                 writeU16(os, 0);
816         }
817         
818         // Versions up from 15 have static objects.
819         if(version >= 15)
820         {
821                 m_static_objects.serialize(os);
822         }
823
824         // Timestamp
825         if(version >= 17)
826         {
827                 writeU32(os, getTimestamp());
828         }
829 }
830
831 void MapBlock::deSerializeDiskExtra(std::istream &is, u8 version)
832 {
833         /*
834                 Versions up from 9 have block objects. (DEPRECATED)
835         */
836         if(version >= 9)
837         {
838                 u16 count = readU16(is);
839                 // Not supported and length not known if count is not 0
840                 if(count != 0){
841                         dstream<<"WARNING: MapBlock::deSerializeDiskExtra(): "
842                                         <<"Ignoring stuff coming at and after MBOs"<<std::endl;
843                         return;
844                 }
845         }
846
847         /*
848                 Versions up from 15 have static objects.
849         */
850         if(version >= 15)
851         {
852                 m_static_objects.deSerialize(is);
853         }
854                 
855         // Timestamp
856         if(version >= 17)
857         {
858                 setTimestamp(readU32(is));
859         }
860         else
861         {
862                 setTimestamp(BLOCK_TIMESTAMP_UNDEFINED);
863         }
864 }
865
866 /*
867         Get a quick string to describe what a block actually contains
868 */
869 std::string analyze_block(MapBlock *block)
870 {
871         if(block == NULL)
872         {
873                 return "NULL";
874         }
875
876         std::ostringstream desc;
877         
878         v3s16 p = block->getPos();
879         char spos[20];
880         snprintf(spos, 20, "(%2d,%2d,%2d), ", p.X, p.Y, p.Z);
881         desc<<spos;
882         
883         switch(block->getModified())
884         {
885         case MOD_STATE_CLEAN:
886                 desc<<"CLEAN,           ";
887                 break;
888         case MOD_STATE_WRITE_AT_UNLOAD:
889                 desc<<"WRITE_AT_UNLOAD, ";
890                 break;
891         case MOD_STATE_WRITE_NEEDED:
892                 desc<<"WRITE_NEEDED,    ";
893                 break;
894         default:
895                 desc<<"unknown getModified()="+itos(block->getModified())+", ";
896         }
897
898         if(block->isGenerated())
899                 desc<<"is_gen [X], ";
900         else
901                 desc<<"is_gen [ ], ";
902
903         if(block->getIsUnderground())
904                 desc<<"is_ug [X], ";
905         else
906                 desc<<"is_ug [ ], ";
907
908 #ifndef SERVER
909         if(block->getMeshExpired())
910                 desc<<"mesh_exp [X], ";
911         else
912                 desc<<"mesh_exp [ ], ";
913 #endif
914
915         if(block->getLightingExpired())
916                 desc<<"lighting_exp [X], ";
917         else
918                 desc<<"lighting_exp [ ], ";
919
920         if(block->isDummy())
921         {
922                 desc<<"Dummy, ";
923         }
924         else
925         {
926                 // We'll just define the numbers here, don't want to include
927                 // content_mapnode.h
928                 const content_t content_water = 2;
929                 const content_t content_watersource = 9;
930                 const content_t content_tree = 0x801;
931                 const content_t content_leaves = 0x802;
932                 const content_t content_jungletree = 0x815;
933
934                 bool full_ignore = true;
935                 bool some_ignore = false;
936                 bool full_air = true;
937                 bool some_air = false;
938                 bool trees = false;
939                 bool water = false;
940                 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
941                 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
942                 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
943                 {
944                         v3s16 p(x0,y0,z0);
945                         MapNode n = block->getNode(p);
946                         content_t c = n.getContent();
947                         if(c == CONTENT_IGNORE)
948                                 some_ignore = true;
949                         else
950                                 full_ignore = false;
951                         if(c == CONTENT_AIR)
952                                 some_air = true;
953                         else
954                                 full_air = false;
955                         if(c == content_tree || c == content_jungletree
956                                         || c == content_leaves)
957                                 trees = true;
958                         if(c == content_water
959                                         || c == content_watersource)
960                                 water = true;
961                 }
962                 
963                 desc<<"content {";
964                 
965                 std::ostringstream ss;
966                 
967                 if(full_ignore)
968                         ss<<"IGNORE (full), ";
969                 else if(some_ignore)
970                         ss<<"IGNORE, ";
971                 
972                 if(full_air)
973                         ss<<"AIR (full), ";
974                 else if(some_air)
975                         ss<<"AIR, ";
976
977                 if(trees)
978                         ss<<"trees, ";
979                 if(water)
980                         ss<<"water, ";
981                 
982                 if(ss.str().size()>=2)
983                         desc<<ss.str().substr(0, ss.str().size()-2);
984
985                 desc<<"}, ";
986         }
987
988         return desc.str().substr(0, desc.str().size()-2);
989 }
990
991
992 //END