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