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