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