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