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