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