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