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