]> git.lizzy.rs Git - minetest.git/blob - src/mapblock.cpp
Now texture handling is fast. Also now players are saved on disk.
[minetest.git] / src / mapblock.cpp
1 /*
2 Minetest-c55
3 Copyright (C) 2010 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "mapblock.h"
21 #include "map.h"
22 // For g_materials
23 #include "main.h"
24 #include "light.h"
25 #include <sstream>
26
27
28 /*
29         MapBlock
30 */
31
32 MapBlock::MapBlock(NodeContainer *parent, v3s16 pos, bool dummy):
33                 m_parent(parent),
34                 m_pos(pos),
35                 changed(true),
36                 is_underground(false),
37                 m_day_night_differs(false),
38                 m_objects(this)
39 {
40         data = NULL;
41         if(dummy == false)
42                 reallocate();
43         
44         m_spawn_timer = -10000;
45
46 #ifndef SERVER
47         m_mesh_expired = false;
48         mesh_mutex.Init();
49         mesh = NULL;
50         m_temp_mods_mutex.Init();
51 #endif
52 }
53
54 MapBlock::~MapBlock()
55 {
56 #ifndef SERVER
57         {
58                 JMutexAutoLock lock(mesh_mutex);
59                 
60                 if(mesh)
61                 {
62                         mesh->drop();
63                         mesh = NULL;
64                 }
65         }
66 #endif
67
68         if(data)
69                 delete[] data;
70 }
71
72 bool MapBlock::isValidPositionParent(v3s16 p)
73 {
74         if(isValidPosition(p))
75         {
76                 return true;
77         }
78         else{
79                 return m_parent->isValidPosition(getPosRelative() + p);
80         }
81 }
82
83 MapNode MapBlock::getNodeParent(v3s16 p)
84 {
85         if(isValidPosition(p) == false)
86         {
87                 return m_parent->getNode(getPosRelative() + p);
88         }
89         else
90         {
91                 if(data == NULL)
92                         throw InvalidPositionException();
93                 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
94         }
95 }
96
97 void MapBlock::setNodeParent(v3s16 p, MapNode & n)
98 {
99         if(isValidPosition(p) == false)
100         {
101                 m_parent->setNode(getPosRelative() + p, n);
102         }
103         else
104         {
105                 if(data == NULL)
106                         throw InvalidPositionException();
107                 data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X] = n;
108         }
109 }
110
111 MapNode MapBlock::getNodeParentNoEx(v3s16 p)
112 {
113         if(isValidPosition(p) == false)
114         {
115                 try{
116                         return m_parent->getNode(getPosRelative() + p);
117                 }
118                 catch(InvalidPositionException &e)
119                 {
120                         return MapNode(CONTENT_IGNORE);
121                 }
122         }
123         else
124         {
125                 if(data == NULL)
126                 {
127                         return MapNode(CONTENT_IGNORE);
128                 }
129                 return data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X];
130         }
131 }
132
133 /*
134         Parameters must consist of air and !air.
135         Order doesn't matter.
136
137         If either of the nodes doesn't exist, light is 0.
138         
139         parameters:
140                 daynight_ratio: 0...1000
141                 n: getNodeParent(p)
142                 n2: getNodeParent(p + face_dir)
143                 face_dir: axis oriented unit vector from p to p2
144         
145         returns encoded light value.
146 */
147 u8 MapBlock::getFaceLight(u32 daynight_ratio, MapNode n, MapNode n2,
148                 v3s16 face_dir)
149 {
150         try{
151                 // DEBUG
152                 /*{
153                         if(n.d == CONTENT_WATER)
154                         {
155                                 u8 l = n.param2*2;
156                                 if(l > LIGHT_MAX)
157                                         l = LIGHT_MAX;
158                                 return l;
159                         }
160                         if(n2.d == CONTENT_WATER)
161                         {
162                                 u8 l = n2.param2*2;
163                                 if(l > LIGHT_MAX)
164                                         l = LIGHT_MAX;
165                                 return l;
166                         }
167                 }*/
168
169
170                 u8 light;
171                 u8 l1 = n.getLightBlend(daynight_ratio);
172                 u8 l2 = n2.getLightBlend(daynight_ratio);
173                 if(l1 > l2)
174                         light = l1;
175                 else
176                         light = l2;
177
178                 // Make some nice difference to different sides
179
180                 /*if(face_dir.X == 1 || face_dir.Z == 1 || face_dir.Y == -1)
181                         light = diminish_light(diminish_light(light));
182                 else if(face_dir.X == -1 || face_dir.Z == -1)
183                         light = diminish_light(light);*/
184
185                 if(face_dir.X == 1 || face_dir.X == -1 || face_dir.Y == -1)
186                         light = diminish_light(diminish_light(light));
187                 else if(face_dir.Z == 1 || face_dir.Z == -1)
188                         light = diminish_light(light);
189
190                 return light;
191         }
192         catch(InvalidPositionException &e)
193         {
194                 return 0;
195         }
196 }
197
198 #ifndef SERVER
199
200 void MapBlock::makeFastFace(TileSpec tile, u8 light, v3f p,
201                 v3s16 dir, v3f scale, v3f posRelative_f,
202                 core::array<FastFace> &dest)
203 {
204         FastFace face;
205         
206         // Position is at the center of the cube.
207         v3f pos = p * BS;
208         posRelative_f *= BS;
209
210         v3f vertex_pos[4];
211         // If looking towards z+, this is the face that is behind
212         // the center point, facing towards z+.
213         vertex_pos[0] = v3f( BS/2,-BS/2,BS/2);
214         vertex_pos[1] = v3f(-BS/2,-BS/2,BS/2);
215         vertex_pos[2] = v3f(-BS/2, BS/2,BS/2);
216         vertex_pos[3] = v3f( BS/2, BS/2,BS/2);
217         
218         if(dir == v3s16(0,0,1))
219         {
220                 for(u16 i=0; i<4; i++)
221                         vertex_pos[i].rotateXZBy(0);
222         }
223         else if(dir == v3s16(0,0,-1))
224         {
225                 for(u16 i=0; i<4; i++)
226                         vertex_pos[i].rotateXZBy(180);
227         }
228         else if(dir == v3s16(1,0,0))
229         {
230                 for(u16 i=0; i<4; i++)
231                         vertex_pos[i].rotateXZBy(-90);
232         }
233         else if(dir == v3s16(-1,0,0))
234         {
235                 for(u16 i=0; i<4; i++)
236                         vertex_pos[i].rotateXZBy(90);
237         }
238         else if(dir == v3s16(0,1,0))
239         {
240                 for(u16 i=0; i<4; i++)
241                         vertex_pos[i].rotateYZBy(-90);
242         }
243         else if(dir == v3s16(0,-1,0))
244         {
245                 for(u16 i=0; i<4; i++)
246                         vertex_pos[i].rotateYZBy(90);
247         }
248
249         for(u16 i=0; i<4; i++)
250         {
251                 vertex_pos[i].X *= scale.X;
252                 vertex_pos[i].Y *= scale.Y;
253                 vertex_pos[i].Z *= scale.Z;
254                 vertex_pos[i] += pos + posRelative_f;
255         }
256
257         f32 abs_scale = 1.;
258         if     (scale.X < 0.999 || scale.X > 1.001) abs_scale = scale.X;
259         else if(scale.Y < 0.999 || scale.Y > 1.001) abs_scale = scale.Y;
260         else if(scale.Z < 0.999 || scale.Z > 1.001) abs_scale = scale.Z;
261
262         v3f zerovector = v3f(0,0,0);
263         
264         //u8 li = decode_light(light);
265         u8 li = light;
266         //u8 li = 255; //DEBUG
267
268         u8 alpha = tile.alpha;
269         /*u8 alpha = 255;
270         if(tile.id == TILE_WATER)
271                 alpha = WATER_ALPHA;*/
272
273         video::SColor c = video::SColor(alpha,li,li,li);
274
275         face.vertices[0] = video::S3DVertex(vertex_pos[0], zerovector, c,
276                         core::vector2d<f32>(0,1));
277         face.vertices[1] = video::S3DVertex(vertex_pos[1], zerovector, c,
278                         core::vector2d<f32>(abs_scale,1));
279         face.vertices[2] = video::S3DVertex(vertex_pos[2], zerovector, c,
280                         core::vector2d<f32>(abs_scale,0));
281         face.vertices[3] = video::S3DVertex(vertex_pos[3], zerovector, c,
282                         core::vector2d<f32>(0,0));
283
284         face.tile = tile;
285         //DEBUG
286         //f->tile = TILE_STONE;
287         
288         dest.push_back(face);
289         //return f;
290 }
291         
292 /*
293         Gets node tile from any place relative to block.
294         Returns TILE_NODE if doesn't exist or should not be drawn.
295 */
296 TileSpec MapBlock::getNodeTile(MapNode mn, v3s16 p, v3s16 face_dir)
297 {
298         TileSpec spec;
299         spec = mn.getTile(face_dir);
300         
301         /*
302                 Check temporary modifications on this node
303         */
304         core::map<v3s16, NodeMod>::Node *n;
305         n = m_temp_mods.find(p);
306
307         // If modified
308         if(n != NULL)
309         {
310                 struct NodeMod mod = n->getValue();
311                 if(mod.type == NODEMOD_CHANGECONTENT)
312                 {
313                         MapNode mn2(mod.param);
314                         spec = mn2.getTile(face_dir);
315                 }
316                 if(mod.type == NODEMOD_CRACK)
317                 {
318                         std::ostringstream os;
319                         os<<"[crack"<<mod.param;
320
321                         textureid_t tid = g_irrlicht->getTextureId(os.str());
322                         spec.spec.addTid(tid);
323                 }
324         }
325         
326         return spec;
327 }
328
329 u8 MapBlock::getNodeContent(v3s16 p, MapNode mn)
330 {
331         /*
332                 Check temporary modifications on this node
333         */
334         core::map<v3s16, NodeMod>::Node *n;
335         n = m_temp_mods.find(p);
336
337         // If modified
338         if(n != NULL)
339         {
340                 struct NodeMod mod = n->getValue();
341                 if(mod.type == NODEMOD_CHANGECONTENT)
342                 {
343                         // Overrides content
344                         return mod.param;
345                 }
346                 if(mod.type == NODEMOD_CRACK)
347                 {
348                         /*
349                                 Content doesn't change.
350                                 
351                                 face_contents works just like it should, because
352                                 there should not be faces between differently cracked
353                                 nodes.
354
355                                 If a semi-transparent node is cracked in front an
356                                 another one, it really doesn't matter whether there
357                                 is a cracked face drawn in between or not.
358                         */
359                 }
360         }
361
362         return mn.d;
363 }
364
365 /*
366         startpos:
367         translate_dir: unit vector with only one of x, y or z
368         face_dir: unit vector with only one of x, y or z
369 */
370 void MapBlock::updateFastFaceRow(
371                 u32 daynight_ratio,
372                 v3f posRelative_f,
373                 v3s16 startpos,
374                 u16 length,
375                 v3s16 translate_dir,
376                 v3f translate_dir_f,
377                 v3s16 face_dir,
378                 v3f face_dir_f,
379                 core::array<FastFace> &dest)
380 {
381         v3s16 p = startpos;
382         
383         u16 continuous_tiles_count = 0;
384         
385         MapNode n0 = getNodeParentNoEx(p);
386         MapNode n1 = getNodeParentNoEx(p + face_dir);
387
388         u8 light = getFaceLight(daynight_ratio, n0, n1, face_dir);
389                 
390         TileSpec tile0 = getNodeTile(n0, p, face_dir);
391         TileSpec tile1 = getNodeTile(n1, p + face_dir, -face_dir);
392
393         for(u16 j=0; j<length; j++)
394         {
395                 bool next_is_different = true;
396                 
397                 v3s16 p_next;
398                 MapNode n0_next;
399                 MapNode n1_next;
400                 TileSpec tile0_next;
401                 TileSpec tile1_next;
402                 u8 light_next = 0;
403
404                 if(j != length - 1)
405                 {
406                         p_next = p + translate_dir;
407                         n0_next = getNodeParentNoEx(p_next);
408                         n1_next = getNodeParentNoEx(p_next + face_dir);
409                         tile0_next = getNodeTile(n0_next, p_next, face_dir);
410                         tile1_next = getNodeTile(n1_next, p_next + face_dir, -face_dir);
411                         light_next = getFaceLight(daynight_ratio, n0_next, n1_next, face_dir);
412
413                         if(tile0_next == tile0
414                                         && tile1_next == tile1
415                                         && light_next == light)
416                         {
417                                 next_is_different = false;
418                         }
419                 }
420
421                 continuous_tiles_count++;
422                 
423                 if(next_is_different)
424                 {
425                         /*
426                                 Create a face if there should be one
427                         */
428                         //u8 mf = face_contents(tile0, tile1);
429                         // This is hackish
430                         u8 content0 = getNodeContent(p, n0);
431                         u8 content1 = getNodeContent(p + face_dir, n1);
432                         u8 mf = face_contents(content0, content1);
433                         
434                         if(mf != 0)
435                         {
436                                 // Floating point conversion of the position vector
437                                 v3f pf(p.X, p.Y, p.Z);
438                                 // Center point of face (kind of)
439                                 v3f sp = pf - ((f32)continuous_tiles_count / 2. - 0.5) * translate_dir_f;
440                                 v3f scale(1,1,1);
441                                 if(translate_dir.X != 0){
442                                         scale.X = continuous_tiles_count;
443                                 }
444                                 if(translate_dir.Y != 0){
445                                         scale.Y = continuous_tiles_count;
446                                 }
447                                 if(translate_dir.Z != 0){
448                                         scale.Z = continuous_tiles_count;
449                                 }
450                                 
451                                 //FastFace *f;
452
453                                 // If node at sp (tile0) is more solid
454                                 if(mf == 1)
455                                 {
456                                         makeFastFace(tile0, decode_light(light),
457                                                         sp, face_dir, scale,
458                                                         posRelative_f, dest);
459                                 }
460                                 // If node at sp is less solid (mf == 2)
461                                 else
462                                 {
463                                         makeFastFace(tile1, decode_light(light),
464                                                         sp+face_dir_f, -face_dir, scale,
465                                                         posRelative_f, dest);
466                                 }
467                                 //dest.push_back(f);
468                         }
469
470                         continuous_tiles_count = 0;
471                         n0 = n0_next;
472                         n1 = n1_next;
473                         tile0 = tile0_next;
474                         tile1 = tile1_next;
475                         light = light_next;
476                 }
477                 
478                 p = p_next;
479         }
480 }
481
482 /*
483         This is used because CMeshBuffer::append() is very slow
484 */
485 struct PreMeshBuffer
486 {
487         video::SMaterial material;
488         core::array<u16> indices;
489         core::array<video::S3DVertex> vertices;
490 };
491
492 class MeshCollector
493 {
494 public:
495         void append(
496                         video::SMaterial material,
497                         const video::S3DVertex* const vertices,
498                         u32 numVertices,
499                         const u16* const indices,
500                         u32 numIndices
501                 )
502         {
503                 PreMeshBuffer *p = NULL;
504                 for(u32 i=0; i<m_prebuffers.size(); i++)
505                 {
506                         PreMeshBuffer &pp = m_prebuffers[i];
507                         if(pp.material != material)
508                                 continue;
509
510                         p = &pp;
511                         break;
512                 }
513
514                 if(p == NULL)
515                 {
516                         PreMeshBuffer pp;
517                         pp.material = material;
518                         m_prebuffers.push_back(pp);
519                         p = &m_prebuffers[m_prebuffers.size()-1];
520                 }
521
522                 u32 vertex_count = p->vertices.size();
523                 for(u32 i=0; i<numIndices; i++)
524                 {
525                         u32 j = indices[i] + vertex_count;
526                         if(j > 65535)
527                         {
528                                 dstream<<"FIXME: Meshbuffer ran out of indices"<<std::endl;
529                                 // NOTE: Fix is to just add an another MeshBuffer
530                         }
531                         p->indices.push_back(j);
532                 }
533                 for(u32 i=0; i<numVertices; i++)
534                 {
535                         p->vertices.push_back(vertices[i]);
536                 }
537         }
538
539         void fillMesh(scene::SMesh *mesh)
540         {
541                 /*dstream<<"Filling mesh with "<<m_prebuffers.size()
542                                 <<" meshbuffers"<<std::endl;*/
543                 for(u32 i=0; i<m_prebuffers.size(); i++)
544                 {
545                         PreMeshBuffer &p = m_prebuffers[i];
546
547                         /*dstream<<"p.vertices.size()="<<p.vertices.size()
548                                         <<", p.indices.size()="<<p.indices.size()
549                                         <<std::endl;*/
550                         
551                         // Create meshbuffer
552                         
553                         // This is a "Standard MeshBuffer",
554                         // it's a typedeffed CMeshBuffer<video::S3DVertex>
555                         scene::SMeshBuffer *buf = new scene::SMeshBuffer();
556                         // Set material
557                         buf->Material = p.material;
558                         //((scene::SMeshBuffer*)buf)->Material = p.material;
559                         // Use VBO
560                         //buf->setHardwareMappingHint(scene::EHM_STATIC);
561                         // Add to mesh
562                         mesh->addMeshBuffer(buf);
563                         // Mesh grabbed it
564                         buf->drop();
565
566                         buf->append(p.vertices.pointer(), p.vertices.size(),
567                                         p.indices.pointer(), p.indices.size());
568                 }
569         }
570
571 private:
572         core::array<PreMeshBuffer> m_prebuffers;
573 };
574
575 void MapBlock::updateMesh(u32 daynight_ratio)
576 {
577 #if 0
578         /*
579                 DEBUG: If mesh has been generated, don't generate it again
580         */
581         {
582                 JMutexAutoLock meshlock(mesh_mutex);
583                 if(mesh != NULL)
584                         return;
585         }
586 #endif
587         
588         // 4-21ms for MAP_BLOCKSIZE=16
589         // 24-155ms for MAP_BLOCKSIZE=32
590         //TimeTaker timer1("updateMesh()");
591
592         core::array<FastFace> fastfaces_new;
593         
594         v3f posRelative_f(getPosRelative().X, getPosRelative().Y,
595                         getPosRelative().Z); // floating point conversion
596         
597         /*
598                 We are including the faces of the trailing edges of the block.
599                 This means that when something changes, the caller must
600                 also update the meshes of the blocks at the leading edges.
601
602                 NOTE: This is the slowest part of this method.
603         */
604         
605         {
606                 // 4-23ms for MAP_BLOCKSIZE=16
607                 //TimeTaker timer2("updateMesh() collect");
608
609                 // Lock this, as m_temp_mods will be used directly
610                 JMutexAutoLock lock(m_temp_mods_mutex);
611
612                 /*
613                         Go through every y,z and get top faces in rows of x+
614                 */
615                 for(s16 y=0; y<MAP_BLOCKSIZE; y++){
616                         for(s16 z=0; z<MAP_BLOCKSIZE; z++){
617                                 updateFastFaceRow(daynight_ratio, posRelative_f,
618                                                 v3s16(0,y,z), MAP_BLOCKSIZE,
619                                                 v3s16(1,0,0), //dir
620                                                 v3f  (1,0,0),
621                                                 v3s16(0,1,0), //face dir
622                                                 v3f  (0,1,0),
623                                                 fastfaces_new);
624                         }
625                 }
626                 /*
627                         Go through every x,y and get right faces in rows of z+
628                 */
629                 for(s16 x=0; x<MAP_BLOCKSIZE; x++){
630                         for(s16 y=0; y<MAP_BLOCKSIZE; y++){
631                                 updateFastFaceRow(daynight_ratio, posRelative_f,
632                                                 v3s16(x,y,0), MAP_BLOCKSIZE,
633                                                 v3s16(0,0,1),
634                                                 v3f  (0,0,1),
635                                                 v3s16(1,0,0),
636                                                 v3f  (1,0,0),
637                                                 fastfaces_new);
638                         }
639                 }
640                 /*
641                         Go through every y,z and get back faces in rows of x+
642                 */
643                 for(s16 z=0; z<MAP_BLOCKSIZE; z++){
644                         for(s16 y=0; y<MAP_BLOCKSIZE; y++){
645                                 updateFastFaceRow(daynight_ratio, posRelative_f,
646                                                 v3s16(0,y,z), MAP_BLOCKSIZE,
647                                                 v3s16(1,0,0),
648                                                 v3f  (1,0,0),
649                                                 v3s16(0,0,1),
650                                                 v3f  (0,0,1),
651                                                 fastfaces_new);
652                         }
653                 }
654         }
655
656         // End of slow part
657
658         /*
659                 Convert FastFaces to SMesh
660         */
661
662         scene::SMesh *mesh_new = NULL;
663         
664         mesh_new = new scene::SMesh();
665         
666         MeshCollector collector;
667
668         if(fastfaces_new.size() > 0)
669         {
670                 // avg 0ms (100ms spikes when loading textures the first time)
671                 //TimeTaker timer2("updateMesh() mesh building");
672
673                 video::SMaterial material;
674                 material.Lighting = false;
675                 material.BackfaceCulling = false;
676                 material.setFlag(video::EMF_BILINEAR_FILTER, false);
677                 material.setFlag(video::EMF_ANTI_ALIASING, video::EAAM_OFF);
678                 material.setFlag(video::EMF_FOG_ENABLE, true);
679
680                 for(u32 i=0; i<fastfaces_new.size(); i++)
681                 {
682                         FastFace &f = fastfaces_new[i];
683
684                         const u16 indices[] = {0,1,2,2,3,0};
685
686                         video::ITexture *texture = g_irrlicht->getTexture(f.tile.spec);
687                         material.setTexture(0, texture);
688                         if(f.tile.alpha != 255)
689                                 material.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
690                         else
691                                 material.MaterialType = video::EMT_SOLID;
692                         
693                         collector.append(material, f.vertices, 4, indices, 6);
694                 }
695         }
696
697         /*
698                 Add special graphics:
699                 - torches
700                 - flowing water
701         */
702
703         // 0ms
704         //TimeTaker timer2("updateMesh() adding special stuff");
705
706         // Flowing water material
707         video::SMaterial material_w1;
708         material_w1.setFlag(video::EMF_LIGHTING, false);
709         material_w1.setFlag(video::EMF_BACK_FACE_CULLING, false);
710         material_w1.setFlag(video::EMF_BILINEAR_FILTER, false);
711         material_w1.setFlag(video::EMF_FOG_ENABLE, true);
712         material_w1.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA;
713         material_w1.setTexture(0,
714                         g_irrlicht->getTexture("water.png"));
715
716         for(s16 z=0; z<MAP_BLOCKSIZE; z++)
717         for(s16 y=0; y<MAP_BLOCKSIZE; y++)
718         for(s16 x=0; x<MAP_BLOCKSIZE; x++)
719         {
720                 v3s16 p(x,y,z);
721
722                 MapNode &n = getNodeRef(x,y,z);
723                 
724                 /*
725                         Add torches to mesh
726                 */
727                 if(n.d == CONTENT_TORCH)
728                 {
729                         video::SColor c(255,255,255,255);
730
731                         video::S3DVertex vertices[4] =
732                         {
733                                 video::S3DVertex(-BS/2,-BS/2,0, 0,0,0, c, 0,1),
734                                 video::S3DVertex(BS/2,-BS/2,0, 0,0,0, c, 1,1),
735                                 video::S3DVertex(BS/2,BS/2,0, 0,0,0, c, 1,0),
736                                 video::S3DVertex(-BS/2,BS/2,0, 0,0,0, c, 0,0),
737                         };
738
739                         v3s16 dir = unpackDir(n.dir);
740
741                         for(s32 i=0; i<4; i++)
742                         {
743                                 if(dir == v3s16(1,0,0))
744                                         vertices[i].Pos.rotateXZBy(0);
745                                 if(dir == v3s16(-1,0,0))
746                                         vertices[i].Pos.rotateXZBy(180);
747                                 if(dir == v3s16(0,0,1))
748                                         vertices[i].Pos.rotateXZBy(90);
749                                 if(dir == v3s16(0,0,-1))
750                                         vertices[i].Pos.rotateXZBy(-90);
751                                 if(dir == v3s16(0,-1,0))
752                                         vertices[i].Pos.rotateXZBy(45);
753                                 if(dir == v3s16(0,1,0))
754                                         vertices[i].Pos.rotateXZBy(-45);
755
756                                 vertices[i].Pos += intToFloat(p + getPosRelative());
757                         }
758
759                         // Set material
760                         video::SMaterial material;
761                         material.setFlag(video::EMF_LIGHTING, false);
762                         material.setFlag(video::EMF_BACK_FACE_CULLING, false);
763                         material.setFlag(video::EMF_BILINEAR_FILTER, false);
764                         //material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL;
765                         material.MaterialType
766                                         = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF;
767                         if(dir == v3s16(0,-1,0))
768                                 material.setTexture(0,
769                                                 g_irrlicht->getTexture("torch_on_floor.png"));
770                         else if(dir == v3s16(0,1,0))
771                                 material.setTexture(0,
772                                                 g_irrlicht->getTexture("torch_on_ceiling.png"));
773                         // For backwards compatibility
774                         else if(dir == v3s16(0,0,0))
775                                 material.setTexture(0,
776                                                 g_irrlicht->getTexture("torch_on_floor.png"));
777                         else
778                                 material.setTexture(0, 
779                                                 g_irrlicht->getTexture("torch.png"));
780
781                         u16 indices[] = {0,1,2,2,3,0};
782                         // Add to mesh collector
783                         collector.append(material, vertices, 4, indices, 6);
784                 }
785                 /*
786                         Add flowing water to mesh
787                 */
788                 else if(n.d == CONTENT_WATER)
789                 {
790                         bool top_is_water = false;
791                         try{
792                                 MapNode n = getNodeParent(v3s16(x,y+1,z));
793                                 if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
794                                         top_is_water = true;
795                         }catch(InvalidPositionException &e){}
796                         
797                         u8 l = decode_light(n.getLightBlend(daynight_ratio));
798                         video::SColor c(WATER_ALPHA,l,l,l);
799                         
800                         // Neighbor water levels (key = relative position)
801                         // Includes current node
802                         core::map<v3s16, f32> neighbor_levels;
803                         core::map<v3s16, u8> neighbor_contents;
804                         v3s16 neighbor_dirs[9] = {
805                                 v3s16(0,0,0),
806                                 v3s16(0,0,1),
807                                 v3s16(0,0,-1),
808                                 v3s16(1,0,0),
809                                 v3s16(-1,0,0),
810                                 v3s16(1,0,1),
811                                 v3s16(-1,0,-1),
812                                 v3s16(1,0,-1),
813                                 v3s16(-1,0,1),
814                         };
815                         for(u32 i=0; i<9; i++)
816                         {
817                                 u8 content = CONTENT_AIR;
818                                 float level = -0.5 * BS;
819                                 try{
820                                         v3s16 p2 = p + neighbor_dirs[i];
821                                         MapNode n2 = getNodeParent(p2);
822
823                                         content = n2.d;
824
825                                         if(n2.d == CONTENT_WATERSOURCE)
826                                                 level = 0.5 * BS;
827                                         else if(n2.d == CONTENT_WATER)
828                                                 level = (-0.5 + ((float)n2.param2 + 0.5) / 8.0) * BS;
829                                 }
830                                 catch(InvalidPositionException &e){}
831                                 
832                                 neighbor_levels.insert(neighbor_dirs[i], level);
833                                 neighbor_contents.insert(neighbor_dirs[i], content);
834                         }
835
836                         //float water_level = (-0.5 + ((float)n.param2 + 0.5) / 8.0) * BS;
837                         //float water_level = neighbor_levels[v3s16(0,0,0)];
838
839                         // Corner heights (average between four waters)
840                         f32 corner_levels[4];
841                         
842                         v3s16 halfdirs[4] = {
843                                 v3s16(0,0,0),
844                                 v3s16(1,0,0),
845                                 v3s16(1,0,1),
846                                 v3s16(0,0,1),
847                         };
848                         for(u32 i=0; i<4; i++)
849                         {
850                                 v3s16 cornerdir = halfdirs[i];
851                                 float cornerlevel = 0;
852                                 u32 valid_count = 0;
853                                 for(u32 j=0; j<4; j++)
854                                 {
855                                         v3s16 neighbordir = cornerdir - halfdirs[j];
856                                         u8 content = neighbor_contents[neighbordir];
857                                         // Special case for source nodes
858                                         if(content == CONTENT_WATERSOURCE)
859                                         {
860                                                 cornerlevel = 0.5*BS;
861                                                 valid_count = 1;
862                                                 break;
863                                         }
864                                         else if(content == CONTENT_WATER)
865                                         {
866                                                 cornerlevel += neighbor_levels[neighbordir];
867                                                 valid_count++;
868                                         }
869                                         else if(content == CONTENT_AIR)
870                                         {
871                                                 cornerlevel += -0.5*BS;
872                                                 valid_count++;
873                                         }
874                                 }
875                                 if(valid_count > 0)
876                                         cornerlevel /= valid_count;
877                                 corner_levels[i] = cornerlevel;
878                         }
879
880                         /*
881                                 Generate sides
882                         */
883
884                         v3s16 side_dirs[4] = {
885                                 v3s16(1,0,0),
886                                 v3s16(-1,0,0),
887                                 v3s16(0,0,1),
888                                 v3s16(0,0,-1),
889                         };
890                         s16 side_corners[4][2] = {
891                                 {1, 2},
892                                 {3, 0},
893                                 {2, 3},
894                                 {0, 1},
895                         };
896                         for(u32 i=0; i<4; i++)
897                         {
898                                 v3s16 dir = side_dirs[i];
899
900                                 //float neighbor_level = neighbor_levels[dir];
901                                 /*if(neighbor_level > -0.5*BS + 0.001)
902                                         continue;*/
903                                 /*if(neighbor_level > water_level - 0.1*BS)
904                                         continue;*/
905
906                                 u8 neighbor_content = neighbor_contents[dir];
907
908                                 if(neighbor_content != CONTENT_AIR
909                                                 && neighbor_content != CONTENT_WATER)
910                                         continue;
911                                 
912                                 bool neighbor_is_water = (neighbor_content == CONTENT_WATER);
913
914                                 if(neighbor_is_water == true && top_is_water == false)
915                                         continue;
916                                 
917                                 video::S3DVertex vertices[4] =
918                                 {
919                                         /*video::S3DVertex(-BS/2,-BS/2,BS/2, 0,0,0, c, 0,1),
920                                         video::S3DVertex(BS/2,-BS/2,BS/2, 0,0,0, c, 1,1),
921                                         video::S3DVertex(BS/2,BS/2,BS/2, 0,0,0, c, 1,0),
922                                         video::S3DVertex(-BS/2,BS/2,BS/2, 0,0,0, c, 0,0),*/
923                                         video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,1),
924                                         video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,1),
925                                         video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
926                                         video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),
927                                 };
928                                 
929                                 if(top_is_water)
930                                 {
931                                         vertices[2].Pos.Y = 0.5*BS;
932                                         vertices[3].Pos.Y = 0.5*BS;
933                                 }
934                                 else
935                                 {
936                                         vertices[2].Pos.Y = corner_levels[side_corners[i][0]];
937                                         vertices[3].Pos.Y = corner_levels[side_corners[i][1]];
938                                 }
939
940                                 if(neighbor_is_water)
941                                 {
942                                         vertices[0].Pos.Y = corner_levels[side_corners[i][1]];
943                                         vertices[1].Pos.Y = corner_levels[side_corners[i][0]];
944                                 }
945                                 else
946                                 {
947                                         vertices[0].Pos.Y = -0.5*BS;
948                                         vertices[1].Pos.Y = -0.5*BS;
949                                 }
950                                 
951                                 for(s32 j=0; j<4; j++)
952                                 {
953                                         if(dir == v3s16(0,0,1))
954                                                 vertices[j].Pos.rotateXZBy(0);
955                                         if(dir == v3s16(0,0,-1))
956                                                 vertices[j].Pos.rotateXZBy(180);
957                                         if(dir == v3s16(-1,0,0))
958                                                 vertices[j].Pos.rotateXZBy(90);
959                                         if(dir == v3s16(1,0,-0))
960                                                 vertices[j].Pos.rotateXZBy(-90);
961
962                                         vertices[j].Pos += intToFloat(p + getPosRelative());
963                                 }
964
965                                 u16 indices[] = {0,1,2,2,3,0};
966                                 // Add to mesh collector
967                                 collector.append(material_w1, vertices, 4, indices, 6);
968                         }
969                         
970                         /*
971                                 Generate top side, if appropriate
972                         */
973                         
974                         if(top_is_water == false)
975                         {
976                                 video::S3DVertex vertices[4] =
977                                 {
978                                         video::S3DVertex(-BS/2,0,-BS/2, 0,0,0, c, 0,1),
979                                         video::S3DVertex(BS/2,0,-BS/2, 0,0,0, c, 1,1),
980                                         video::S3DVertex(BS/2,0,BS/2, 0,0,0, c, 1,0),
981                                         video::S3DVertex(-BS/2,0,BS/2, 0,0,0, c, 0,0),
982                                 };
983
984                                 for(s32 i=0; i<4; i++)
985                                 {
986                                         //vertices[i].Pos.Y += water_level;
987                                         //vertices[i].Pos.Y += neighbor_levels[v3s16(0,0,0)];
988                                         vertices[i].Pos.Y += corner_levels[i];
989                                         vertices[i].Pos += intToFloat(p + getPosRelative());
990                                 }
991
992                                 u16 indices[] = {0,1,2,2,3,0};
993                                 // Add to mesh collector
994                                 collector.append(material_w1, vertices, 4, indices, 6);
995                         }
996                 }
997         }
998
999         /*
1000                 Add stuff from collector to mesh
1001         */
1002         
1003         collector.fillMesh(mesh_new);
1004
1005         /*
1006                 Do some stuff to the mesh
1007         */
1008
1009         mesh_new->recalculateBoundingBox();
1010
1011         /*
1012                 Delete new mesh if it is empty
1013         */
1014
1015         if(mesh_new->getMeshBufferCount() == 0)
1016         {
1017                 mesh_new->drop();
1018                 mesh_new = NULL;
1019         }
1020
1021         // Use VBO for mesh (this just would set this for ever buffer)
1022         // This will lead to infinite memory usage because or irrlicht.
1023         //mesh_new->setHardwareMappingHint(scene::EHM_STATIC);
1024         
1025         /*std::cout<<"MapBlock has "<<fastfaces_new.size()<<" faces "
1026                         <<"and uses "<<mesh_new->getMeshBufferCount()
1027                         <<" materials (meshbuffers)"<<std::endl;*/
1028         
1029         /*
1030                 Replace the mesh
1031         */
1032
1033         mesh_mutex.Lock();
1034
1035         //scene::SMesh *mesh_old = mesh[daynight_i];
1036         //mesh[daynight_i] = mesh_new;
1037
1038         scene::SMesh *mesh_old = mesh;
1039         mesh = mesh_new;
1040         setMeshExpired(false);
1041         
1042         if(mesh_old != NULL)
1043         {
1044                 // Remove hardware buffers of meshbuffers of mesh
1045                 // NOTE: No way, this runs in a different thread and everything
1046                 /*u32 c = mesh_old->getMeshBufferCount();
1047                 for(u32 i=0; i<c; i++)
1048                 {
1049                         IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
1050                 }*/
1051                 
1052                 /*dstream<<"mesh_old->getReferenceCount()="
1053                                 <<mesh_old->getReferenceCount()<<std::endl;
1054                 u32 c = mesh_old->getMeshBufferCount();
1055                 for(u32 i=0; i<c; i++)
1056                 {
1057                         scene::IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
1058                         dstream<<"buf->getReferenceCount()="
1059                                         <<buf->getReferenceCount()<<std::endl;
1060                 }*/
1061
1062                 // Drop the mesh
1063                 mesh_old->drop();
1064
1065                 //delete mesh_old;
1066         }
1067
1068         mesh_mutex.Unlock();
1069         
1070         //std::cout<<"added "<<fastfaces.getSize()<<" faces."<<std::endl;
1071 }
1072
1073 /*void MapBlock::updateMeshes(s32 first_i)
1074 {
1075         assert(first_i >= 0 && first_i <= DAYNIGHT_CACHE_COUNT);
1076         updateMesh(first_i);
1077         for(s32 i=0; i<DAYNIGHT_CACHE_COUNT; i++)
1078         {
1079                 if(i == first_i)
1080                         continue;
1081                 updateMesh(i);
1082         }
1083 }*/
1084
1085 #endif // !SERVER
1086
1087 /*
1088         Propagates sunlight down through the block.
1089         Doesn't modify nodes that are not affected by sunlight.
1090         
1091         Returns false if sunlight at bottom block is invalid
1092         Returns true if bottom block doesn't exist.
1093
1094         If there is a block above, continues from it.
1095         If there is no block above, assumes there is sunlight, unless
1096         is_underground is set or highest node is water.
1097
1098         At the moment, all sunlighted nodes are added to light_sources.
1099         - SUGG: This could be optimized
1100
1101         Turns sunglighted mud into grass.
1102
1103         if remove_light==true, sets non-sunlighted nodes black.
1104
1105         if black_air_left!=NULL, it is set to true if non-sunlighted
1106         air is left in block.
1107 */
1108 bool MapBlock::propagateSunlight(core::map<v3s16, bool> & light_sources,
1109                 bool remove_light, bool *black_air_left,
1110                 bool grow_grass)
1111 {
1112         // Whether the sunlight at the top of the bottom block is valid
1113         bool block_below_is_valid = true;
1114         
1115         v3s16 pos_relative = getPosRelative();
1116         
1117         for(s16 x=0; x<MAP_BLOCKSIZE; x++)
1118         {
1119                 for(s16 z=0; z<MAP_BLOCKSIZE; z++)
1120                 {
1121 #if 1
1122                         bool no_sunlight = false;
1123                         bool no_top_block = false;
1124                         // Check if node above block has sunlight
1125                         try{
1126                                 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
1127                                 if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN)
1128                                 {
1129                                         no_sunlight = true;
1130                                 }
1131                         }
1132                         catch(InvalidPositionException &e)
1133                         {
1134                                 no_top_block = true;
1135                                 
1136                                 // NOTE: This makes over-ground roofed places sunlighted
1137                                 // Assume sunlight, unless is_underground==true
1138                                 if(is_underground)
1139                                 {
1140                                         no_sunlight = true;
1141                                 }
1142                                 else
1143                                 {
1144                                         MapNode n = getNode(v3s16(x, MAP_BLOCKSIZE-1, z));
1145                                         if(n.d == CONTENT_WATER || n.d == CONTENT_WATERSOURCE)
1146                                         {
1147                                                 no_sunlight = true;
1148                                         }
1149                                 }
1150                                 // NOTE: As of now, this just would make everything dark.
1151                                 // No sunlight here
1152                                 //no_sunlight = true;
1153                         }
1154 #endif
1155 #if 0 // Doesn't work; nothing gets light.
1156                         bool no_sunlight = true;
1157                         bool no_top_block = false;
1158                         // Check if node above block has sunlight
1159                         try{
1160                                 MapNode n = getNodeParent(v3s16(x, MAP_BLOCKSIZE, z));
1161                                 if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN)
1162                                 {
1163                                         no_sunlight = false;
1164                                 }
1165                         }
1166                         catch(InvalidPositionException &e)
1167                         {
1168                                 no_top_block = true;
1169                         }
1170 #endif
1171
1172                         /*std::cout<<"("<<x<<","<<z<<"): "
1173                                         <<"no_top_block="<<no_top_block
1174                                         <<", is_underground="<<is_underground
1175                                         <<", no_sunlight="<<no_sunlight
1176                                         <<std::endl;*/
1177                 
1178                         s16 y = MAP_BLOCKSIZE-1;
1179                         
1180                         // This makes difference to diminishing in water.
1181                         bool stopped_to_solid_object = false;
1182                         
1183                         u8 current_light = no_sunlight ? 0 : LIGHT_SUN;
1184
1185                         for(; y >= 0; y--)
1186                         {
1187                                 v3s16 pos(x, y, z);
1188                                 MapNode &n = getNodeRef(pos);
1189                                 
1190                                 if(current_light == 0)
1191                                 {
1192                                         // Do nothing
1193                                 }
1194                                 else if(current_light == LIGHT_SUN && n.sunlight_propagates())
1195                                 {
1196                                         // Do nothing: Sunlight is continued
1197                                 }
1198                                 else if(n.light_propagates() == false)
1199                                 {
1200                                         if(grow_grass)
1201                                         {
1202                                                 bool upper_is_air = false;
1203                                                 try
1204                                                 {
1205                                                         if(getNodeParent(pos+v3s16(0,1,0)).d == CONTENT_AIR)
1206                                                                 upper_is_air = true;
1207                                                 }
1208                                                 catch(InvalidPositionException &e)
1209                                                 {
1210                                                 }
1211                                                 // Turn mud into grass
1212                                                 if(upper_is_air && n.d == CONTENT_MUD
1213                                                                 && current_light == LIGHT_SUN)
1214                                                 {
1215                                                         n.d = CONTENT_GRASS;
1216                                                 }
1217                                         }
1218
1219                                         // A solid object is on the way.
1220                                         stopped_to_solid_object = true;
1221                                         
1222                                         // Light stops.
1223                                         current_light = 0;
1224                                 }
1225                                 else
1226                                 {
1227                                         // Diminish light
1228                                         current_light = diminish_light(current_light);
1229                                 }
1230
1231                                 u8 old_light = n.getLight(LIGHTBANK_DAY);
1232
1233                                 if(current_light > old_light || remove_light)
1234                                 {
1235                                         n.setLight(LIGHTBANK_DAY, current_light);
1236                                 }
1237                                 
1238                                 if(diminish_light(current_light) != 0)
1239                                 {
1240                                         light_sources.insert(pos_relative + pos, true);
1241                                 }
1242
1243                                 if(current_light == 0 && stopped_to_solid_object)
1244                                 {
1245                                         if(black_air_left)
1246                                         {
1247                                                 *black_air_left = true;
1248                                         }
1249                                 }
1250                         }
1251
1252                         // Whether or not the block below should see LIGHT_SUN
1253                         bool sunlight_should_go_down = (current_light == LIGHT_SUN);
1254
1255                         /*
1256                                 If the block below hasn't already been marked invalid:
1257
1258                                 Check if the node below the block has proper sunlight at top.
1259                                 If not, the block below is invalid.
1260                                 
1261                                 Ignore non-transparent nodes as they always have no light
1262                         */
1263                         try
1264                         {
1265                         if(block_below_is_valid)
1266                         {
1267                                 MapNode n = getNodeParent(v3s16(x, -1, z));
1268                                 if(n.light_propagates())
1269                                 {
1270                                         if(n.getLight(LIGHTBANK_DAY) == LIGHT_SUN
1271                                                         && sunlight_should_go_down == false)
1272                                                 block_below_is_valid = false;
1273                                         else if(n.getLight(LIGHTBANK_DAY) != LIGHT_SUN
1274                                                         && sunlight_should_go_down == true)
1275                                                 block_below_is_valid = false;
1276                                 }
1277                         }//if
1278                         }//try
1279                         catch(InvalidPositionException &e)
1280                         {
1281                                 /*std::cout<<"InvalidBlockException for bottom block node"
1282                                                 <<std::endl;*/
1283                                 // Just no block below, no need to panic.
1284                         }
1285                 }
1286         }
1287
1288         return block_below_is_valid;
1289 }
1290
1291 void MapBlock::copyTo(VoxelManipulator &dst)
1292 {
1293         v3s16 data_size(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE);
1294         VoxelArea data_area(v3s16(0,0,0), data_size - v3s16(1,1,1));
1295         
1296         dst.copyFrom(data, data_area, v3s16(0,0,0),
1297                         getPosRelative(), data_size);
1298 }
1299
1300 /*void getPseudoObjects(v3f origin, f32 max_d,
1301                 core::array<DistanceSortedObject> &dest)
1302 {
1303 }*/
1304 void MapBlock::stepObjects(float dtime, bool server, u32 daynight_ratio)
1305 {
1306         /*
1307                 Step objects
1308         */
1309         m_objects.step(dtime, server, daynight_ratio);
1310         
1311         /*
1312                 Spawn some objects at random.
1313
1314                 Use dayNightDiffed() to approximate being near ground level
1315         */
1316         if(m_spawn_timer < -999)
1317         {
1318                 m_spawn_timer = 60;
1319         }
1320         if(dayNightDiffed() == true && getObjectCount() == 0)
1321         {
1322                 m_spawn_timer -= dtime;
1323                 if(m_spawn_timer <= 0.0)
1324                 {
1325                         m_spawn_timer += myrand() % 300;
1326                         
1327                         v2s16 p2d(
1328                                 (myrand()%(MAP_BLOCKSIZE-1))+0,
1329                                 (myrand()%(MAP_BLOCKSIZE-1))+0
1330                         );
1331
1332                         s16 y = getGroundLevel(p2d);
1333                         
1334                         if(y >= 0)
1335                         {
1336                                 v3s16 p(p2d.X, y+1, p2d.Y);
1337
1338                                 if(getNode(p).d == CONTENT_AIR
1339                                                 && getNode(p).getLightBlend(daynight_ratio) <= 11)
1340                                 {
1341                                         RatObject *obj = new RatObject(NULL, -1, intToFloat(p));
1342                                         addObject(obj);
1343                                 }
1344                         }
1345                 }
1346         }
1347
1348         setChangedFlag();
1349 }
1350
1351
1352 void MapBlock::updateDayNightDiff()
1353 {
1354         if(data == NULL)
1355         {
1356                 m_day_night_differs = false;
1357                 return;
1358         }
1359
1360         bool differs = false;
1361
1362         /*
1363                 Check if any lighting value differs
1364         */
1365         for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1366         {
1367                 MapNode &n = data[i];
1368                 if(n.getLight(LIGHTBANK_DAY) != n.getLight(LIGHTBANK_NIGHT))
1369                 {
1370                         differs = true;
1371                         break;
1372                 }
1373         }
1374
1375         /*
1376                 If some lighting values differ, check if the whole thing is
1377                 just air. If it is, differ = false
1378         */
1379         if(differs)
1380         {
1381                 bool only_air = true;
1382                 for(u32 i=0; i<MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE; i++)
1383                 {
1384                         MapNode &n = data[i];
1385                         if(n.d != CONTENT_AIR)
1386                         {
1387                                 only_air = false;
1388                                 break;
1389                         }
1390                 }
1391                 if(only_air)
1392                         differs = false;
1393         }
1394
1395         // Set member variable
1396         m_day_night_differs = differs;
1397 }
1398
1399 s16 MapBlock::getGroundLevel(v2s16 p2d)
1400 {
1401         if(isDummy())
1402                 return -3;
1403         try
1404         {
1405                 s16 y = MAP_BLOCKSIZE-1;
1406                 for(; y>=0; y--)
1407                 {
1408                         //if(is_ground_content(getNodeRef(p2d.X, y, p2d.Y).d))
1409                         if(content_features(getNodeRef(p2d.X, y, p2d.Y).d).walkable)
1410                         {
1411                                 if(y == MAP_BLOCKSIZE-1)
1412                                         return -2;
1413                                 else
1414                                         return y;
1415                         }
1416                 }
1417                 return -1;
1418         }
1419         catch(InvalidPositionException &e)
1420         {
1421                 return -3;
1422         }
1423 }
1424
1425 /*
1426         Serialization
1427 */
1428
1429 void MapBlock::serialize(std::ostream &os, u8 version)
1430 {
1431         if(!ser_ver_supported(version))
1432                 throw VersionMismatchException("ERROR: MapBlock format not supported");
1433         
1434         if(data == NULL)
1435         {
1436                 throw SerializationError("ERROR: Not writing dummy block.");
1437         }
1438         
1439         // These have no compression
1440         if(version <= 3 || version == 5 || version == 6)
1441         {
1442                 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1443                 
1444                 u32 buflen = 1 + nodecount * MapNode::serializedLength(version);
1445                 SharedBuffer<u8> dest(buflen);
1446
1447                 dest[0] = is_underground;
1448                 for(u32 i=0; i<nodecount; i++)
1449                 {
1450                         u32 s = 1 + i * MapNode::serializedLength(version);
1451                         data[i].serialize(&dest[s], version);
1452                 }
1453                 
1454                 os.write((char*)*dest, dest.getSize());
1455         }
1456         else if(version <= 10)
1457         {
1458                 /*
1459                         With compression.
1460                         Compress the materials and the params separately.
1461                 */
1462                 
1463                 // First byte
1464                 os.write((char*)&is_underground, 1);
1465
1466                 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1467
1468                 // Get and compress materials
1469                 SharedBuffer<u8> materialdata(nodecount);
1470                 for(u32 i=0; i<nodecount; i++)
1471                 {
1472                         materialdata[i] = data[i].d;
1473                 }
1474                 compress(materialdata, os, version);
1475
1476                 // Get and compress lights
1477                 SharedBuffer<u8> lightdata(nodecount);
1478                 for(u32 i=0; i<nodecount; i++)
1479                 {
1480                         lightdata[i] = data[i].param;
1481                 }
1482                 compress(lightdata, os, version);
1483                 
1484                 if(version >= 10)
1485                 {
1486                         // Get and compress param2
1487                         SharedBuffer<u8> param2data(nodecount);
1488                         for(u32 i=0; i<nodecount; i++)
1489                         {
1490                                 param2data[i] = data[i].param2;
1491                         }
1492                         compress(param2data, os, version);
1493                 }
1494         }
1495         // All other versions (newest)
1496         else
1497         {
1498                 // First byte
1499                 u8 flags = 0;
1500                 if(is_underground)
1501                         flags |= 1;
1502                 if(m_day_night_differs)
1503                         flags |= 2;
1504                 os.write((char*)&flags, 1);
1505
1506                 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1507
1508                 /*
1509                         Get data
1510                 */
1511
1512                 SharedBuffer<u8> databuf(nodecount*3);
1513
1514                 // Get contents
1515                 for(u32 i=0; i<nodecount; i++)
1516                 {
1517                         databuf[i] = data[i].d;
1518                 }
1519
1520                 // Get params
1521                 for(u32 i=0; i<nodecount; i++)
1522                 {
1523                         databuf[i+nodecount] = data[i].param;
1524                 }
1525
1526                 // Get param2
1527                 for(u32 i=0; i<nodecount; i++)
1528                 {
1529                         databuf[i+nodecount*2] = data[i].param2;
1530                 }
1531
1532                 /*
1533                         Compress data to output stream
1534                 */
1535
1536                 compress(databuf, os, version);
1537         }
1538 }
1539
1540 void MapBlock::deSerialize(std::istream &is, u8 version)
1541 {
1542         if(!ser_ver_supported(version))
1543                 throw VersionMismatchException("ERROR: MapBlock format not supported");
1544
1545         // These have no compression
1546         if(version <= 3 || version == 5 || version == 6)
1547         {
1548                 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1549                 char tmp;
1550                 is.read(&tmp, 1);
1551                 if(is.gcount() != 1)
1552                         throw SerializationError
1553                                         ("MapBlock::deSerialize: no enough input data");
1554                 is_underground = tmp;
1555                 for(u32 i=0; i<nodecount; i++)
1556                 {
1557                         s32 len = MapNode::serializedLength(version);
1558                         SharedBuffer<u8> d(len);
1559                         is.read((char*)*d, len);
1560                         if(is.gcount() != len)
1561                                 throw SerializationError
1562                                                 ("MapBlock::deSerialize: no enough input data");
1563                         data[i].deSerialize(*d, version);
1564                 }
1565         }
1566         else if(version <= 10)
1567         {
1568                 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1569
1570                 u8 t8;
1571                 is.read((char*)&t8, 1);
1572                 is_underground = t8;
1573
1574                 {
1575                         // Uncompress and set material data
1576                         std::ostringstream os(std::ios_base::binary);
1577                         decompress(is, os, version);
1578                         std::string s = os.str();
1579                         if(s.size() != nodecount)
1580                                 throw SerializationError
1581                                                 ("MapBlock::deSerialize: invalid format");
1582                         for(u32 i=0; i<s.size(); i++)
1583                         {
1584                                 data[i].d = s[i];
1585                         }
1586                 }
1587                 {
1588                         // Uncompress and set param data
1589                         std::ostringstream os(std::ios_base::binary);
1590                         decompress(is, os, version);
1591                         std::string s = os.str();
1592                         if(s.size() != nodecount)
1593                                 throw SerializationError
1594                                                 ("MapBlock::deSerialize: invalid format");
1595                         for(u32 i=0; i<s.size(); i++)
1596                         {
1597                                 data[i].param = s[i];
1598                         }
1599                 }
1600         
1601                 if(version >= 10)
1602                 {
1603                         // Uncompress and set param2 data
1604                         std::ostringstream os(std::ios_base::binary);
1605                         decompress(is, os, version);
1606                         std::string s = os.str();
1607                         if(s.size() != nodecount)
1608                                 throw SerializationError
1609                                                 ("MapBlock::deSerialize: invalid format");
1610                         for(u32 i=0; i<s.size(); i++)
1611                         {
1612                                 data[i].param2 = s[i];
1613                         }
1614                 }
1615         }
1616         // All other versions (newest)
1617         else
1618         {
1619                 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
1620
1621                 u8 flags;
1622                 is.read((char*)&flags, 1);
1623                 is_underground = (flags & 1) ? true : false;
1624                 m_day_night_differs = (flags & 2) ? true : false;
1625
1626                 // Uncompress data
1627                 std::ostringstream os(std::ios_base::binary);
1628                 decompress(is, os, version);
1629                 std::string s = os.str();
1630                 if(s.size() != nodecount*3)
1631                         throw SerializationError
1632                                         ("MapBlock::deSerialize: invalid format");
1633
1634                 // Set contents
1635                 for(u32 i=0; i<nodecount; i++)
1636                 {
1637                         data[i].d = s[i];
1638                 }
1639                 // Set params
1640                 for(u32 i=0; i<nodecount; i++)
1641                 {
1642                         data[i].param = s[i+nodecount];
1643                 }
1644                 // Set param2
1645                 for(u32 i=0; i<nodecount; i++)
1646                 {
1647                         data[i].param2 = s[i+nodecount*2];
1648                 }
1649         }
1650 }
1651
1652
1653 //END