WIP node metadata, node timers
[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 #include "content_nodemeta.h" // For legacy deserialization
34 #ifndef SERVER
35 #include "mapblock_mesh.h"
36 #endif
37
38 /*
39         MapBlock
40 */
41
42 MapBlock::MapBlock(Map *parent, v3s16 pos, IGameDef *gamedef, bool dummy):
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_day_night_differs_expired(true),
53                 m_generated(false),
54                 m_timestamp(BLOCK_TIMESTAMP_UNDEFINED),
55                 m_disk_timestamp(BLOCK_TIMESTAMP_UNDEFINED),
56                 m_usage_timer(0)
57 {
58         data = NULL;
59         if(dummy == false)
60                 reallocate();
61         
62 #ifndef SERVER
63         //mesh_mutex.Init();
64         mesh = NULL;
65 #endif
66 }
67
68 MapBlock::~MapBlock()
69 {
70 #ifndef SERVER
71         {
72                 //JMutexAutoLock lock(mesh_mutex);
73                 
74                 if(mesh)
75                 {
76                         delete mesh;
77                         mesh = NULL;
78                 }
79         }
80 #endif
81
82         if(data)
83                 delete[] data;
84 }
85
86 bool MapBlock::isValidPositionParent(v3s16 p)
87 {
88         if(isValidPosition(p))
89         {
90                 return true;
91         }
92         else{
93                 return m_parent->isValidPosition(getPosRelative() + p);
94         }
95 }
96
97 MapNode MapBlock::getNodeParent(v3s16 p)
98 {
99         if(isValidPosition(p) == false)
100         {
101                 return m_parent->getNode(getPosRelative() + p);
102         }
103         else
104         {
105                 if(data == NULL)
106                         throw InvalidPositionException();
107                 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
108         }
109 }
110
111 void MapBlock::setNodeParent(v3s16 p, MapNode & n)
112 {
113         if(isValidPosition(p) == false)
114         {
115                 m_parent->setNode(getPosRelative() + p, n);
116         }
117         else
118         {
119                 if(data == NULL)
120                         throw InvalidPositionException();
121                 data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X] = n;
122         }
123 }
124
125 MapNode MapBlock::getNodeParentNoEx(v3s16 p)
126 {
127         if(isValidPosition(p) == false)
128         {
129                 try{
130                         return m_parent->getNode(getPosRelative() + p);
131                 }
132                 catch(InvalidPositionException &e)
133                 {
134                         return MapNode(CONTENT_IGNORE);
135                 }
136         }
137         else
138         {
139                 if(data == NULL)
140                 {
141                         return MapNode(CONTENT_IGNORE);
142                 }
143                 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
144         }
145 }
146
147 /*
148         Propagates sunlight down through the block.
149         Doesn't modify nodes that are not affected by sunlight.
150         
151         Returns false if sunlight at bottom block is invalid.
152         Returns true if sunlight at bottom block is valid.
153         Returns true if bottom block doesn't exist.
154
155         If there is a block above, continues from it.
156         If there is no block above, assumes there is sunlight, unless
157         is_underground is set or highest node is water.
158
159         All sunlighted nodes are added to light_sources.
160
161         if remove_light==true, sets non-sunlighted nodes black.
162
163         if black_air_left!=NULL, it is set to true if non-sunlighted
164         air is left in block.
165 */
166 bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources,
167                 bool remove_light, bool *black_air_left)
168 {
169         INodeDefManager *nodemgr = m_gamedef->ndef();
170
171         // Whether the sunlight at the top of the bottom block is valid
172         bool block_below_is_valid = true;
173         
174         v3s16 pos_relative = getPosRelative();
175         
176         for(s16 x=0; x<MAP_BLOCKSIZE; x++)
177         {
178                 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
179                 {
180 #if 1
181                         bool no_sunlight = false;
182                         bool no_top_block = false;
183                         // Check if node above block has sunlight
184                         try{
185                                 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
186                                 if(n.getContent() == CONTENT_IGNORE)
187                                 {
188                                         // Trust heuristics
189                                         no_sunlight = is_underground;
190                                 }
191                                 else if(n.getLight(LIGHTBANK_DAY, m_gamedef->ndef()) != LIGHT_SUN)
192                                 {
193                                         no_sunlight = true;
194                                 }
195                         }
196                         catch(InvalidPositionException &e)
197                         {
198                                 no_top_block = true;
199                                 
200                                 // NOTE: This makes over-ground roofed places sunlighted
201                                 // Assume sunlight, unless is_underground==true
202                                 if(is_underground)
203                                 {
204                                         no_sunlight = true;
205                                 }
206                                 else
207                                 {
208                                         MapNode n = getNode(v3s16(x, MAP_BLOCKSIZE-1, z));
209                                         if(m_gamedef->ndef()->get(n).sunlight_propagates == false)
210                                         {
211                                                 no_sunlight = true;
212                                         }
213                                 }
214                                 // NOTE: As of now, this just would make everything dark.
215                                 // No sunlight here
216                                 //no_sunlight = true;
217                         }
218 #endif
219 #if 0 // Doesn't work; nothing gets light.
220                         bool no_sunlight = true;
221                         bool no_top_block = false;
222                         // Check if node above block has sunlight
223                         try{
224                                 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
225                                 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
226                                 {
227                                         no_sunlight = false;
228                                 }
229                         }
230                         catch(InvalidPositionException &e)
231                         {
232                                 no_top_block = true;
233                         }
234 #endif
235
236                         /*std::cout<<"("<<x<<","<<z<<"): "
237                                         <<"no_top_block="<<no_top_block
238                                         <<", is_underground="<<is_underground
239                                         <<", no_sunlight="<<no_sunlight
240                                         <<std::endl;*/
241                 
242                         s16 y = MAP_BLOCKSIZE-1;
243                         
244                         // This makes difference to diminishing in water.
245                         bool stopped_to_solid_object = false;
246                         
247                         u8 current_light = no_sunlight ? 0 : LIGHT_SUN;
248
249                         for(; y >= 0; y--)
250                         {
251                                 v3s16 pos(x, y, z);
252                                 MapNode &n = getNodeRef(pos);
253                                 
254                                 if(current_light == 0)
255                                 {
256                                         // Do nothing
257                                 }
258                                 else if(current_light == LIGHT_SUN && nodemgr->get(n).sunlight_propagates)
259                                 {
260                                         // Do nothing: Sunlight is continued
261                                 }
262                                 else if(nodemgr->get(n).light_propagates == false)
263                                 {
264                                         // A solid object is on the way.
265                                         stopped_to_solid_object = true;
266                                         
267                                         // Light stops.
268                                         current_light = 0;
269                                 }
270                                 else
271                                 {
272                                         // Diminish light
273                                         current_light = diminish_light(current_light);
274                                 }
275
276                                 u8 old_light = n.getLight(LIGHTBANK_DAY, nodemgr);
277
278                                 if(current_light > old_light || remove_light)
279                                 {
280                                         n.setLight(LIGHTBANK_DAY, current_light, nodemgr);
281                                 }
282                                 
283                                 if(diminish_light(current_light) != 0)
284                                 {
285                                         light_sources.insert(pos_relative + pos, true);
286                                 }
287
288                                 if(current_light == 0 && stopped_to_solid_object)
289                                 {
290                                         if(black_air_left)
291                                         {
292                                                 *black_air_left = true;
293                                         }
294                                 }
295                         }
296
297                         // Whether or not the block below should see LIGHT_SUN
298                         bool sunlight_should_go_down = (current_light == LIGHT_SUN);
299
300                         /*
301                                 If the block below hasn't already been marked invalid:
302
303                                 Check if the node below the block has proper sunlight at top.
304                                 If not, the block below is invalid.
305                                 
306                                 Ignore non-transparent nodes as they always have no light
307                         */
308                         try
309                         {
310                         if(block_below_is_valid)
311                         {
312                                 MapNode n = getNodeParent(v3s16(x, -1, z));
313                                 if(nodemgr->get(n).light_propagates)
314                                 {
315                                         if(n.getLight(LIGHTBANK_DAY, nodemgr) == LIGHT_SUN
316                                                         && sunlight_should_go_down == false)
317                                                 block_below_is_valid = false;
318                                         else if(n.getLight(LIGHTBANK_DAY, nodemgr) != LIGHT_SUN
319                                                         && sunlight_should_go_down == true)
320                                                 block_below_is_valid = false;
321                                 }
322                         }//if
323                         }//try
324                         catch(InvalidPositionException &e)
325                         {
326                                 /*std::cout<<"InvalidBlockException for bottom block node"
327                                                 <<std::endl;*/
328                                 // Just no block below, no need to panic.
329                         }
330                 }
331         }
332
333         return block_below_is_valid;
334 }
335
336
337 void MapBlock::copyTo(VoxelManipulator &dst)
338 {
339         v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
340         VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
341         
342         // Copy from data to VoxelManipulator
343         dst.copyFrom(data, data_area, v3s16(0,0,0),
344                         getPosRelative(), data_size);
345 }
346
347 void MapBlock::copyFrom(VoxelManipulator &dst)
348 {
349         v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
350         VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
351         
352         // Copy from VoxelManipulator to data
353         dst.copyTo(data, data_area, v3s16(0,0,0),
354                         getPosRelative(), data_size);
355 }
356
357 void MapBlock::actuallyUpdateDayNightDiff()
358 {
359         INodeDefManager *nodemgr = m_gamedef->ndef();
360         // Running this function un-expires m_day_night_differs
361         m_day_night_differs_expired = false;
362
363         if(data == NULL)
364         {
365                 m_day_night_differs = false;
366                 return;
367         }
368
369         bool differs = false;
370
371         /*
372                 Check if any lighting value differs
373         */
374         for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
375         {
376                 MapNode &n = data[i];
377                 if(n.getLight(LIGHTBANK_DAY, nodemgr) != n.getLight(LIGHTBANK_NIGHT, nodemgr))
378                 {
379                         differs = true;
380                         break;
381                 }
382         }
383
384         /*
385                 If some lighting values differ, check if the whole thing is
386                 just air. If it is, differ = false
387         */
388         if(differs)
389         {
390                 bool only_air = true;
391                 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
392                 {
393                         MapNode &n = data[i];
394                         if(n.getContent() != CONTENT_AIR)
395                         {
396                                 only_air = false;
397                                 break;
398                         }
399                 }
400                 if(only_air)
401                         differs = false;
402         }
403
404         // Set member variable
405         m_day_night_differs = differs;
406 }
407
408 void MapBlock::expireDayNightDiff()
409 {
410         INodeDefManager *nodemgr = m_gamedef->ndef();
411
412         if(data == NULL){
413                 m_day_night_differs = false;
414                 m_day_night_differs_expired = false;
415                 return;
416         }
417
418         m_day_night_differs_expired = true;
419 }
420
421 s16 MapBlock::getGroundLevel(v2s16 p2d)
422 {
423         if(isDummy())
424                 return -3;
425         try
426         {
427                 s16 y = MAP_BLOCKSIZE-1;
428                 for(; y>=0; y--)
429                 {
430                         MapNode n = getNodeRef(p2d.X, y, p2d.Y);
431                         if(m_gamedef->ndef()->get(n).walkable)
432                         {
433                                 if(y == MAP_BLOCKSIZE-1)
434                                         return -2;
435                                 else
436                                         return y;
437                         }
438                 }
439                 return -1;
440         }
441         catch(InvalidPositionException &e)
442         {
443                 return -3;
444         }
445 }
446
447 /*
448         Serialization
449 */
450 // List relevant id-name pairs for ids in the block using nodedef
451 // Renumbers the content IDs (starting at 0 and incrementing
452 static void getBlockNodeIdMapping(NameIdMapping *nimap, MapNode *nodes,
453                 INodeDefManager *nodedef)
454 {
455         std::map<content_t, content_t> mapping;
456         std::set<content_t> unknown_contents;
457         content_t id_counter = 0;
458         for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
459         {
460                 content_t global_id = nodes[i].getContent();
461                 content_t id = CONTENT_IGNORE;
462
463                 // Try to find an existing mapping
464                 std::map<content_t, content_t>::iterator j = mapping.find(global_id);
465                 if(j != mapping.end())
466                 {
467                         id = j->second;
468                 }
469                 else
470                 {
471                         // We have to assign a new mapping
472                         id = id_counter++;
473                         mapping.insert(std::make_pair(global_id, id));
474
475                         const ContentFeatures &f = nodedef->get(global_id);
476                         const std::string &name = f.name;
477                         if(name == "")
478                                 unknown_contents.insert(global_id);
479                         else
480                                 nimap->set(id, name);
481                 }
482
483                 // Update the MapNode
484                 nodes[i].setContent(id);
485         }
486         for(std::set<content_t>::const_iterator
487                         i = unknown_contents.begin();
488                         i != unknown_contents.end(); i++){
489                 errorstream<<"getBlockNodeIdMapping(): IGNORING ERROR: "
490                                 <<"Name for node id "<<(*i)<<" not known"<<std::endl;
491         }
492 }
493 // Correct ids in the block to match nodedef based on names.
494 // Unknown ones are added to nodedef.
495 // Will not update itself to match id-name pairs in nodedef.
496 static void correctBlockNodeIds(const NameIdMapping *nimap, MapNode *nodes,
497                 IGameDef *gamedef)
498 {
499         INodeDefManager *nodedef = gamedef->ndef();
500         // This means the block contains incorrect ids, and we contain
501         // the information to convert those to names.
502         // nodedef contains information to convert our names to globally
503         // correct ids.
504         std::set<content_t> unnamed_contents;
505         std::set<std::string> unallocatable_contents;
506         for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
507         {
508                 content_t local_id = nodes[i].getContent();
509                 std::string name;
510                 bool found = nimap->getName(local_id, name);
511                 if(!found){
512                         unnamed_contents.insert(local_id);
513                         continue;
514                 }
515                 content_t global_id;
516                 found = nodedef->getId(name, global_id);
517                 if(!found){
518                         global_id = gamedef->allocateUnknownNodeId(name);
519                         if(global_id == CONTENT_IGNORE){
520                                 unallocatable_contents.insert(name);
521                                 continue;
522                         }
523                 }
524                 nodes[i].setContent(global_id);
525         }
526         for(std::set<content_t>::const_iterator
527                         i = unnamed_contents.begin();
528                         i != unnamed_contents.end(); i++){
529                 errorstream<<"correctBlockNodeIds(): IGNORING ERROR: "
530                                 <<"Block contains id "<<(*i)
531                                 <<" with no name mapping"<<std::endl;
532         }
533         for(std::set<std::string>::const_iterator
534                         i = unallocatable_contents.begin();
535                         i != unallocatable_contents.end(); i++){
536                 errorstream<<"correctBlockNodeIds(): IGNORING ERROR: "
537                                 <<"Could not allocate global id for node name \""
538                                 <<(*i)<<"\""<<std::endl;
539         }
540 }
541
542 void MapBlock::serialize(std::ostream &os, u8 version, bool disk)
543 {
544         if(!ser_ver_supported(version))
545                 throw VersionMismatchException("ERROR: MapBlock format not supported");
546         
547         if(data == NULL)
548         {
549                 throw SerializationError("ERROR: Not writing dummy block.");
550         }
551         
552         if(version <= 21)
553         {
554                 serialize_pre22(os, version, disk);
555                 return;
556         }
557
558         // First byte
559         u8 flags = 0;
560         if(is_underground)
561                 flags |= 0x01;
562         if(getDayNightDiff())
563                 flags |= 0x02;
564         if(m_lighting_expired)
565                 flags |= 0x04;
566         if(m_generated == false)
567                 flags |= 0x08;
568         writeU8(os, flags);
569         
570         /*
571                 Bulk node data
572         */
573         NameIdMapping nimap;
574         u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
575         if(disk)
576         {
577                 MapNode *tmp_nodes = new MapNode[nodecount];
578                 for(u32 i=0; i<nodecount; i++)
579                         tmp_nodes[i] = data[i];
580                 getBlockNodeIdMapping(&nimap, tmp_nodes, m_gamedef->ndef());
581
582                 u8 content_width = 1;
583                 /*u8 content_width = (nimap.size() <= 255) ? 1 : 2;*/
584                 u8 params_width = 2;
585                 writeU8(os, content_width);
586                 writeU8(os, params_width);
587                 MapNode::serializeBulk(os, version, tmp_nodes, nodecount,
588                                 content_width, params_width, true);
589                 delete[] tmp_nodes;
590         }
591         else
592         {
593                 u8 content_width = 1;
594                 /*u8 content_width = 2;*/
595                 u8 params_width = 2;
596                 writeU8(os, content_width);
597                 writeU8(os, params_width);
598                 MapNode::serializeBulk(os, version, data, nodecount,
599                                 content_width, params_width, true);
600         }
601         
602         /*
603                 Node metadata
604         */
605         std::ostringstream oss(std::ios_base::binary);
606         if(version >= 23)
607                 m_node_metadata.serialize(oss);
608         else
609                 content_nodemeta_serialize_legacy(oss, &m_node_metadata);
610         compressZlib(oss.str(), os);
611
612         /*
613                 Data that goes to disk, but not the network
614         */
615         if(disk)
616         {
617                 // Node timers
618                 if(version >= 23)
619                         m_node_timers.serialize(os);
620
621                 // Static objects
622                 m_static_objects.serialize(os);
623
624                 // Timestamp
625                 writeU32(os, getTimestamp());
626
627                 // Write block-specific node definition id mapping
628                 nimap.serialize(os);
629         }
630 }
631
632
633 void MapBlock::deSerialize(std::istream &is, u8 version, bool disk)
634 {
635         if(!ser_ver_supported(version))
636                 throw VersionMismatchException("ERROR: MapBlock format not supported");
637
638         m_day_night_differs_expired = false;
639
640         if(version <= 21)
641         {
642                 deSerialize_pre22(is, version, disk);
643                 return;
644         }
645
646         u8 flags = readU8(is);
647         is_underground = (flags & 0x01) ? true : false;
648         m_day_night_differs = (flags & 0x02) ? true : false;
649         m_lighting_expired = (flags & 0x04) ? true : false;
650         m_generated = (flags & 0x08) ? false : true;
651
652         /*
653                 Bulk node data
654         */
655         u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
656         u8 content_width = readU8(is);
657         u8 params_width = readU8(is);
658         if(content_width != 1)
659                 throw SerializationError("MapBlock::deSerialize(): invalid content_width");
660         if(params_width != 2)
661                 throw SerializationError("MapBlock::deSerialize(): invalid params_width");
662         MapNode::deSerializeBulk(is, version, data, nodecount,
663                         content_width, params_width, true);
664
665         /*
666                 NodeMetadata
667         */
668         // Ignore errors
669         try{
670                 std::ostringstream oss(std::ios_base::binary);
671                 decompressZlib(is, oss);
672                 std::istringstream iss(oss.str(), std::ios_base::binary);
673                 if(version >= 23)
674                         m_node_metadata.deSerialize(iss, m_gamedef);
675                 else
676                         content_nodemeta_deserialize_legacy(iss,
677                                         &m_node_metadata, &m_node_timers,
678                                         m_gamedef);
679         }
680         catch(SerializationError &e)
681         {
682                 errorstream<<"WARNING: MapBlock::deSerialize(): Ignoring an error"
683                                 <<" while deserializing node metadata: "<<e.what()<<std::endl;
684         }
685
686         /*
687                 Data that is only on disk
688         */
689         if(disk)
690         {
691                 // Node timers
692                 if(version >= 23)
693                         m_node_timers.deSerialize(is);
694
695                 // Static objects
696                 m_static_objects.deSerialize(is);
697                 
698                 // Timestamp
699                 setTimestamp(readU32(is));
700                 m_disk_timestamp = m_timestamp;
701                 
702                 // Dynamically re-set ids based on node names
703                 NameIdMapping nimap;
704                 nimap.deSerialize(is);
705                 correctBlockNodeIds(&nimap, data, m_gamedef);
706         }
707 }
708
709 /*
710         Legacy serialization
711 */
712
713 // List relevant id-name pairs for ids in the block using nodedef
714 // Before serialization version 22
715 static void getBlockNodeIdMapping_pre22(NameIdMapping *nimap, MapNode *nodes,
716                 INodeDefManager *nodedef)
717 {
718         std::set<content_t> unknown_contents;
719         for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
720         {
721                 content_t id = nodes[i].getContent();
722                 const ContentFeatures &f = nodedef->get(id);
723                 const std::string &name = f.name;
724                 if(name == "")
725                         unknown_contents.insert(id);
726                 else
727                         nimap->set(id, name);
728         }
729         for(std::set<content_t>::const_iterator
730                         i = unknown_contents.begin();
731                         i != unknown_contents.end(); i++){
732                 errorstream<<"getBlockNodeIdMapping_pre22(): IGNORING ERROR: "
733                                 <<"Name for node id "<<(*i)<<" not known"<<std::endl;
734         }
735 }
736 void MapBlock::serialize_pre22(std::ostream &os, u8 version, bool disk)
737 {
738         u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
739
740         MapNode *tmp_data = new MapNode[nodecount];
741         
742         // Legacy data changes
743         // This code has to change from post-22 to pre-22 format.
744         INodeDefManager *nodedef = m_gamedef->ndef();
745         for(u32 i=0; i<nodecount; i++)
746         {
747                 const ContentFeatures &f = nodedef->get(tmp_data[i].getContent());
748                 // Mineral
749                 if(nodedef->getId("default:stone_with_coal") == tmp_data[i].getContent())
750                 {
751                         tmp_data[i].setContent(nodedef->getId("default:stone"));
752                         tmp_data[i].setParam1(1);  // MINERAL_COAL
753                 }
754                 else if(nodedef->getId("default:stone_with_iron") == tmp_data[i].getContent())
755                 {
756                         tmp_data[i].setContent(nodedef->getId("default:stone"));
757                         tmp_data[i].setParam1(2);  // MINERAL_IRON
758                 }
759                 // facedir_simple
760                 if(f.legacy_facedir_simple)
761                 {
762                         tmp_data[i].setParam1(tmp_data[i].getParam2());
763                         tmp_data[i].setParam2(0);
764                 }
765                 // wall_mounted
766                 if(f.legacy_wallmounted)
767                 {
768                         u8 wallmounted_new_to_old[8] = {0x04, 0x08, 0x01, 0x02, 0x10, 0x20, 0, 0};
769                         u8 dir_new_format = tmp_data[i].getParam2() & 7; // lowest 3 bits
770                         u8 dir_old_format = wallmounted_new_to_old[dir_new_format];
771                         tmp_data[i].setParam2(dir_old_format);
772                 }
773         }
774
775         // Serialize nodes
776         u32 ser_length = MapNode::serializedLength(version);
777         SharedBuffer<u8> databuf_nodelist(nodecount * ser_length);
778         for(u32 i=0; i<nodecount; i++)
779         {
780                 tmp_data[i].serialize(&databuf_nodelist[i*ser_length], version);
781         }
782
783         delete[] tmp_data;
784                 
785         // These have no compression
786         if(version <= 3 || version == 5 || version == 6)
787         {
788                 writeU8(os, is_underground);
789                 os.write((char*)*databuf_nodelist, databuf_nodelist.getSize());
790         }
791         else if(version <= 10)
792         {
793                 /*
794                         With compression.
795                         Compress the materials and the params separately.
796                 */
797                 
798                 // First byte
799                 writeU8(os, is_underground);
800
801                 // Get and compress materials
802                 SharedBuffer<u8> materialdata(nodecount);
803                 for(u32 i=0; i<nodecount; i++)
804                 {
805                         materialdata[i] = databuf_nodelist[i*ser_length];
806                 }
807                 compress(materialdata, os, version);
808
809                 // Get and compress lights
810                 SharedBuffer<u8> lightdata(nodecount);
811                 for(u32 i=0; i<nodecount; i++)
812                 {
813                         lightdata[i] = databuf_nodelist[i*ser_length+1];
814                 }
815                 compress(lightdata, os, version);
816                 
817                 if(version >= 10)
818                 {
819                         // Get and compress param2
820                         SharedBuffer<u8> param2data(nodecount);
821                         for(u32 i=0; i<nodecount; i++)
822                         {
823                                 param2data[i] = databuf_nodelist[i*ser_length+2];
824                         }
825                         compress(param2data, os, version);
826                 }
827         }
828         // All other versions (newest)
829         else
830         {
831                 // First byte
832                 u8 flags = 0;
833                 if(is_underground)
834                         flags |= 0x01;
835                 if(getDayNightDiff())
836                         flags |= 0x02;
837                 if(m_lighting_expired)
838                         flags |= 0x04;
839                 if(version >= 18)
840                 {
841                         if(m_generated == false)
842                                 flags |= 0x08;
843                 }
844                 writeU8(os, flags);
845                 
846                 /*
847                         Get data
848                 */
849
850                 // Create buffer with different parameters sorted
851                 SharedBuffer<u8> databuf(nodecount*3);
852                 for(u32 i=0; i<nodecount; i++)
853                 {
854                         databuf[i] = databuf_nodelist[i*ser_length];
855                         databuf[i+nodecount] = databuf_nodelist[i*ser_length+1];
856                         databuf[i+nodecount*2] = databuf_nodelist[i*ser_length+2];
857                 }
858
859                 /*
860                         Compress data to output stream
861                 */
862
863                 compress(databuf, os, version);
864                 
865                 /*
866                         NodeMetadata
867                 */
868                 if(version >= 14)
869                 {
870                         if(version <= 15)
871                         {
872                                 try{
873                                         std::ostringstream oss(std::ios_base::binary);
874                                         content_nodemeta_serialize_legacy(oss, &m_node_metadata);
875                                         os<<serializeString(oss.str());
876                                 }
877                                 // This will happen if the string is longer than 65535
878                                 catch(SerializationError &e)
879                                 {
880                                         // Use an empty string
881                                         os<<serializeString("");
882                                 }
883                         }
884                         else
885                         {
886                                 std::ostringstream oss(std::ios_base::binary);
887                                 content_nodemeta_serialize_legacy(oss, &m_node_metadata);
888                                 compressZlib(oss.str(), os);
889                                 //os<<serializeLongString(oss.str());
890                         }
891                 }
892         }
893
894
895         if(disk)
896         {
897                 // Versions up from 9 have block objects. (DEPRECATED)
898                 if(version >= 9)
899                 {
900                         // count=0
901                         writeU16(os, 0);
902                 }
903
904                 // Versions up from 15 have static objects.
905                 if(version >= 15)
906                 {
907                         m_static_objects.serialize(os);
908                 }
909
910                 // Timestamp
911                 if(version >= 17)
912                 {
913                         writeU32(os, getTimestamp());
914                 }
915
916                 // Scan and write node definition id mapping
917                 if(version >= 21)
918                 {
919                         NameIdMapping nimap;
920                         getBlockNodeIdMapping_pre22(&nimap, data, m_gamedef->ndef());
921                         nimap.serialize(os);
922                 }
923         }
924 }
925
926 void MapBlock::deSerialize_pre22(std::istream &is, u8 version, bool disk)
927 {
928         u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
929
930         // Initialize default flags
931         is_underground = false;
932         m_day_night_differs = false;
933         m_lighting_expired = false;
934         m_generated = true;
935
936         // Make a temporary buffer
937         u32 ser_length = MapNode::serializedLength(version);
938         SharedBuffer<u8> databuf_nodelist(nodecount * ser_length);
939
940         // These have no compression
941         if(version <= 3 || version == 5 || version == 6)
942         {
943                 char tmp;
944                 is.read(&tmp, 1);
945                 if(is.gcount() != 1)
946                         throw SerializationError
947                                         ("MapBlock::deSerialize: no enough input data");
948                 is_underground = tmp;
949                 is.read((char*)*databuf_nodelist, nodecount * ser_length);
950                 if(is.gcount() != nodecount * ser_length)
951                         throw SerializationError
952                                         ("MapBlock::deSerialize: no enough input data");
953         }
954         else if(version <= 10)
955         {
956                 u8 t8;
957                 is.read((char*)&t8, 1);
958                 is_underground = t8;
959
960                 {
961                         // Uncompress and set material data
962                         std::ostringstream os(std::ios_base::binary);
963                         decompress(is, os, version);
964                         std::string s = os.str();
965                         if(s.size() != nodecount)
966                                 throw SerializationError
967                                                 ("MapBlock::deSerialize: invalid format");
968                         for(u32 i=0; i<s.size(); i++)
969                         {
970                                 databuf_nodelist[i*ser_length] = s[i];
971                         }
972                 }
973                 {
974                         // Uncompress and set param data
975                         std::ostringstream os(std::ios_base::binary);
976                         decompress(is, os, version);
977                         std::string s = os.str();
978                         if(s.size() != nodecount)
979                                 throw SerializationError
980                                                 ("MapBlock::deSerialize: invalid format");
981                         for(u32 i=0; i<s.size(); i++)
982                         {
983                                 databuf_nodelist[i*ser_length + 1] = s[i];
984                         }
985                 }
986         
987                 if(version >= 10)
988                 {
989                         // Uncompress and set param2 data
990                         std::ostringstream os(std::ios_base::binary);
991                         decompress(is, os, version);
992                         std::string s = os.str();
993                         if(s.size() != nodecount)
994                                 throw SerializationError
995                                                 ("MapBlock::deSerialize: invalid format");
996                         for(u32 i=0; i<s.size(); i++)
997                         {
998                                 databuf_nodelist[i*ser_length + 2] = s[i];
999                         }
1000                 }
1001         }
1002         // All other versions (newest)
1003         else
1004         {
1005                 u8 flags;
1006                 is.read((char*)&flags, 1);
1007                 is_underground = (flags & 0x01) ? true : false;
1008                 m_day_night_differs = (flags & 0x02) ? true : false;
1009                 m_lighting_expired = (flags & 0x04) ? true : false;
1010                 if(version >= 18)
1011                         m_generated = (flags & 0x08) ? false : true;
1012
1013                 // Uncompress data
1014                 std::ostringstream os(std::ios_base::binary);
1015                 decompress(is, os, version);
1016                 std::string s = os.str();
1017                 if(s.size() != nodecount*3)
1018                         throw SerializationError
1019                                         ("MapBlock::deSerialize: decompress resulted in size"
1020                                         " other than nodecount*3");
1021
1022                 // deserialize nodes from buffer
1023                 for(u32 i=0; i<nodecount; i++)
1024                 {
1025                         databuf_nodelist[i*ser_length] = s[i];
1026                         databuf_nodelist[i*ser_length + 1] = s[i+nodecount];
1027                         databuf_nodelist[i*ser_length + 2] = s[i+nodecount*2];
1028                 }
1029                 
1030                 /*
1031                         NodeMetadata
1032                 */
1033                 if(version >= 14)
1034                 {
1035                         // Ignore errors
1036                         try{
1037                                 if(version <= 15)
1038                                 {
1039                                         std::string data = deSerializeString(is);
1040                                         std::istringstream iss(data, std::ios_base::binary);
1041                                         content_nodemeta_deserialize_legacy(iss,
1042                                                         &m_node_metadata, &m_node_timers,
1043                                                         m_gamedef);
1044                                 }
1045                                 else
1046                                 {
1047                                         //std::string data = deSerializeLongString(is);
1048                                         std::ostringstream oss(std::ios_base::binary);
1049                                         decompressZlib(is, oss);
1050                                         std::istringstream iss(oss.str(), std::ios_base::binary);
1051                                         content_nodemeta_deserialize_legacy(iss,
1052                                                         &m_node_metadata, &m_node_timers,
1053                                                         m_gamedef);
1054                                 }
1055                         }
1056                         catch(SerializationError &e)
1057                         {
1058                                 errorstream<<"WARNING: MapBlock::deSerialize(): Ignoring an error"
1059                                                 <<" while deserializing node metadata"<<std::endl;
1060                         }
1061                 }
1062         }
1063
1064         // Deserialize node data
1065         for(u32 i=0; i<nodecount; i++)
1066         {
1067                 data[i].deSerialize(&databuf_nodelist[i*ser_length], version);
1068         }
1069
1070         if(disk)
1071         {
1072                 /*
1073                         Versions up from 9 have block objects. (DEPRECATED)
1074                 */
1075                 if(version >= 9){
1076                         u16 count = readU16(is);
1077                         // Not supported and length not known if count is not 0
1078                         if(count != 0){
1079                                 errorstream<<"WARNING: MapBlock::deSerialize_pre22(): "
1080                                                 <<"Ignoring stuff coming at and after MBOs"<<std::endl;
1081                                 return;
1082                         }
1083                 }
1084
1085                 /*
1086                         Versions up from 15 have static objects.
1087                 */
1088                 if(version >= 15)
1089                         m_static_objects.deSerialize(is);
1090
1091                 // Timestamp
1092                 if(version >= 17){
1093                         setTimestamp(readU32(is));
1094                         m_disk_timestamp = m_timestamp;
1095                 } else {
1096                         setTimestamp(BLOCK_TIMESTAMP_UNDEFINED);
1097                 }
1098
1099                 // Dynamically re-set ids based on node names
1100                 NameIdMapping nimap;
1101                 // If supported, read node definition id mapping
1102                 if(version >= 21){
1103                         nimap.deSerialize(is);
1104                 // Else set the legacy mapping
1105                 } else {
1106                         content_mapnode_get_name_id_mapping(&nimap);
1107                 }
1108                 correctBlockNodeIds(&nimap, data, m_gamedef);
1109         }
1110
1111
1112         // Legacy data changes
1113         // This code has to convert from pre-22 to post-22 format.
1114         INodeDefManager *nodedef = m_gamedef->ndef();
1115         for(u32 i=0; i<nodecount; i++)
1116         {
1117                 const ContentFeatures &f = nodedef->get(data[i].getContent());
1118                 // Mineral
1119                 if(nodedef->getId("default:stone") == data[i].getContent()
1120                                 && data[i].getParam1() == 1)
1121                 {
1122                         data[i].setContent(nodedef->getId("default:stone_with_coal"));
1123                         data[i].setParam1(0);
1124                 }
1125                 else if(nodedef->getId("default:stone") == data[i].getContent()
1126                                 && data[i].getParam1() == 2)
1127                 {
1128                         data[i].setContent(nodedef->getId("default:stone_with_iron"));
1129                         data[i].setParam1(0);
1130                 }
1131                 // facedir_simple
1132                 if(f.legacy_facedir_simple)
1133                 {
1134                         data[i].setParam2(data[i].getParam1());
1135                         data[i].setParam1(0);
1136                 }
1137                 // wall_mounted
1138                 if(f.legacy_wallmounted)
1139                 {
1140                         u8 wallmounted_new_to_old[8] = {0x04, 0x08, 0x01, 0x02, 0x10, 0x20, 0, 0};
1141                         u8 dir_old_format = data[i].getParam2();
1142                         u8 dir_new_format = 0;
1143                         for(u8 j=0; j<8; j++)
1144                         {
1145                                 if((dir_old_format & wallmounted_new_to_old[j]) != 0)
1146                                 {
1147                                         dir_new_format = j;
1148                                         break;
1149                                 }
1150                         }
1151                         data[i].setParam2(dir_new_format);
1152                 }
1153         }
1154
1155 }
1156
1157 /*
1158         Get a quick string to describe what a block actually contains
1159 */
1160 std::string analyze_block(MapBlock *block)
1161 {
1162         if(block == NULL)
1163                 return "NULL";
1164
1165         std::ostringstream desc;
1166         
1167         v3s16 p = block->getPos();
1168         char spos[20];
1169         snprintf(spos, 20, "(%2d,%2d,%2d), ", p.X, p.Y, p.Z);
1170         desc<<spos;
1171         
1172         switch(block->getModified())
1173         {
1174         case MOD_STATE_CLEAN:
1175                 desc<<"CLEAN,           ";
1176                 break;
1177         case MOD_STATE_WRITE_AT_UNLOAD:
1178                 desc<<"WRITE_AT_UNLOAD, ";
1179                 break;
1180         case MOD_STATE_WRITE_NEEDED:
1181                 desc<<"WRITE_NEEDED,    ";
1182                 break;
1183         default:
1184                 desc<<"unknown getModified()="+itos(block->getModified())+", ";
1185         }
1186
1187         if(block->isGenerated())
1188                 desc<<"is_gen [X], ";
1189         else
1190                 desc<<"is_gen [ ], ";
1191
1192         if(block->getIsUnderground())
1193                 desc<<"is_ug [X], ";
1194         else
1195                 desc<<"is_ug [ ], ";
1196
1197         if(block->getLightingExpired())
1198                 desc<<"lighting_exp [X], ";
1199         else
1200                 desc<<"lighting_exp [ ], ";
1201
1202         if(block->isDummy())
1203         {
1204                 desc<<"Dummy, ";
1205         }
1206         else
1207         {
1208                 bool full_ignore = true;
1209                 bool some_ignore = false;
1210                 bool full_air = true;
1211                 bool some_air = false;
1212                 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
1213                 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
1214                 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
1215                 {
1216                         v3s16 p(x0,y0,z0);
1217                         MapNode n = block->getNode(p);
1218                         content_t c = n.getContent();
1219                         if(c == CONTENT_IGNORE)
1220                                 some_ignore = true;
1221                         else
1222                                 full_ignore = false;
1223                         if(c == CONTENT_AIR)
1224                                 some_air = true;
1225                         else
1226                                 full_air = false;
1227                 }
1228                 
1229                 desc<<"content {";
1230                 
1231                 std::ostringstream ss;
1232                 
1233                 if(full_ignore)
1234                         ss<<"IGNORE (full), ";
1235                 else if(some_ignore)
1236                         ss<<"IGNORE, ";
1237                 
1238                 if(full_air)
1239                         ss<<"AIR (full), ";
1240                 else if(some_air)
1241                         ss<<"AIR, ";
1242                 
1243                 if(ss.str().size()>=2)
1244                         desc<<ss.str().substr(0, ss.str().size()-2);
1245
1246                 desc<<"}, ";
1247         }
1248
1249         return desc.str().substr(0, desc.str().size()-2);
1250 }
1251
1252
1253 //END