1 /************************************************************************
3 * Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
6 * voxelands - 3d voxel world sandbox game
7 * Copyright (C) Lisa 'darkrose' Milne 2013-2014 <lisa@ltmnet.com>
9 * This program is free software: you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation, either version 3 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17 * See the GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program. If not, see <http://www.gnu.org/licenses/>
22 * License updated from GPLv2 or later to GPLv3 or later by Lisa Milne
24 ************************************************************************/
40 MapBlock::MapBlock(Map
*parent
, v3s16 pos
, bool dummy
):
43 m_modified(MOD_STATE_WRITE_NEEDED
),
44 is_underground(false),
45 m_lighting_expired(true),
46 m_day_night_differs(false),
48 m_timestamp(BLOCK_TIMESTAMP_UNDEFINED
),
55 //m_spawn_timer = -10000;
58 m_mesh_expired
= false;
61 m_temp_mods_mutex
.Init();
69 JMutexAutoLock
lock(mesh_mutex
);
76 for (std::map
<v3s16
,MapBlockSound
>::iterator i
= m_sounds
.begin(); i
!= m_sounds
.end(); i
++) {
77 g_sound
->stopSound(i
->second
.id
);
87 bool MapBlock::isValidPositionParent(v3s16 p
)
89 if (isValidPosition(p
.X
,p
.Y
,p
.Z
))
91 return m_parent
->isValidPosition(getPosRelative() + p
);
94 MapNode
MapBlock::getNodeParent(v3s16 p
, bool *is_valid_position
)
96 if (isValidPosition(p
.X
,p
.Y
,p
.Z
) == false)
97 return m_parent
->getNodeNoEx(getPosRelative() + p
, is_valid_position
);
99 if (is_valid_position
)
100 *is_valid_position
= false;
101 return MapNode(CONTENT_IGNORE
);
103 if (is_valid_position
)
104 *is_valid_position
= true;
105 return data
[p
.Z
*MAP_BLOCKSIZE
*MAP_BLOCKSIZE
+ p
.Y
*MAP_BLOCKSIZE
+ p
.X
];
108 void MapBlock::setNodeParent(v3s16 p
, MapNode
& n
)
110 if (isValidPosition(p
.X
,p
.Y
,p
.Z
) == false) {
111 m_parent
->setNode(getPosRelative() + p
, n
);
113 data
[p
.Z
*MAP_BLOCKSIZE
*MAP_BLOCKSIZE
+ p
.Y
*MAP_BLOCKSIZE
+ p
.X
] = n
;
120 void MapBlock::updateMesh(u32 daynight_ratio
, Environment
*env
, v3s16 camera_offset
)
124 DEBUG: If mesh has been generated, don't generate it again
127 JMutexAutoLock
meshlock(mesh_mutex
);
135 data
.fill(daynight_ratio
, this);
136 data
.m_sounds
= &m_sounds
;
138 MapBlockMesh
*mesh_new
= new MapBlockMesh(&data
, camera_offset
);
140 //scene::SMesh *mesh_new = makeMapBlockMesh(&data);
146 replaceMesh(mesh_new
);
151 void MapBlock::replaceMesh(MapBlockMesh
*mesh_new
)
155 //scene::SMesh *mesh_old = mesh[daynight_i];
156 //mesh[daynight_i] = mesh_new;
158 MapBlockMesh
*mesh_old
= mesh
;
160 setMeshExpired(false);
164 // Remove hardware buffers of meshbuffers of mesh
165 // NOTE: No way, this runs in a different thread and everything
166 /*u32 c = mesh_old->getMeshBufferCount();
167 for(u32 i=0; i<c; i++)
169 IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
172 /*dstream<<"mesh_old->getReferenceCount()="
173 <<mesh_old->getReferenceCount()<<std::endl;
174 u32 c = mesh_old->getMeshBufferCount();
175 for(u32 i=0; i<c; i++)
177 scene::IMeshBuffer *buf = mesh_old->getMeshBuffer(i);
178 dstream<<"buf->getReferenceCount()="
179 <<buf->getReferenceCount()<<std::endl;
194 Propagates sunlight down through the block.
195 Doesn't modify nodes that are not affected by sunlight.
197 Returns false if sunlight at bottom block is invalid.
198 Returns true if sunlight at bottom block is valid.
199 Returns true if bottom block doesn't exist.
201 If there is a block above, continues from it.
202 If there is no block above, assumes there is sunlight, unless
203 is_underground is set or highest node is water.
205 All sunlighted nodes are added to light_sources.
207 if remove_light==true, sets non-sunlighted nodes black.
209 if black_air_left!=NULL, it is set to true if non-sunlighted
210 air is left in block.
212 bool MapBlock::propagateSunlight(core::map
<v3s16
, bool> & light_sources
,
213 bool remove_light
, bool *black_air_left
)
215 // Whether the sunlight at the top of the bottom block is valid
216 bool block_below_is_valid
= true;
218 v3s16 pos_relative
= getPosRelative();
220 for (s16 x
=0; x
<MAP_BLOCKSIZE
; x
++) {
221 for (s16 z
=0; z
<MAP_BLOCKSIZE
; z
++) {
222 bool no_sunlight
= false;
223 //bool no_top_block = false;
224 // Check if node above block has sunlight
225 bool is_valid_position
;
226 MapNode np
= getNodeParent(v3s16(x
, MAP_BLOCKSIZE
, z
), &is_valid_position
);
227 if (is_valid_position
) {
228 if (np
.getContent() == CONTENT_IGNORE
) {
230 no_sunlight
= is_underground
;
231 }else if (np
.getLight(LIGHTBANK_DAY
) != LIGHT_SUN
) {
235 //no_top_block = true;
237 // NOTE: This makes over-ground roofed places sunlighted
238 // Assume sunlight, unless is_underground==true
239 if (is_underground
) {
242 MapNode n
= getNodeNoEx(v3s16(x
, MAP_BLOCKSIZE
-1, z
));
243 //if(n.getContent() == CONTENT_WATER || n.getContent() == CONTENT_WATERSOURCE)
244 if (content_features(n
).sunlight_propagates
== false)
247 // NOTE: As of now, this just would make everything dark.
249 //no_sunlight = true;
252 s16 y
= MAP_BLOCKSIZE
-1;
254 // This makes difference to diminishing in water.
255 bool stopped_to_solid_object
= false;
257 u8 current_light
= no_sunlight
? 0 : LIGHT_SUN
;
259 for (; y
>= 0; y
--) {
261 MapNode
&n
= getNodeRef(pos
);
262 ContentFeatures
&f
= content_features(n
);
264 if (current_light
!= 0 && (current_light
!= LIGHT_SUN
|| !f
.sunlight_propagates
)) {
265 if (!f
.light_propagates
) {
266 // A solid object is on the way.
267 stopped_to_solid_object
= true;
273 current_light
= diminish_light(current_light
);
277 u8 old_light
= n
.getLight(LIGHTBANK_DAY
);
279 if (current_light
> old_light
|| remove_light
)
280 n
.setLight(LIGHTBANK_DAY
, current_light
);
282 if (diminish_light(current_light
) != 0)
283 light_sources
.insert(pos_relative
+ pos
, true);
285 if (current_light
== 0 && stopped_to_solid_object
&& black_air_left
)
286 *black_air_left
= true;
289 // Whether or not the block below should see LIGHT_SUN
290 bool sunlight_should_go_down
= (current_light
== LIGHT_SUN
);
293 If the block below hasn't already been marked invalid:
295 Check if the node below the block has proper sunlight at top.
296 If not, the block below is invalid.
298 Ignore non-transparent nodes as they always have no light
301 if (block_below_is_valid
) {
302 MapNode n
= getNodeParent(v3s16(x
, -1, z
));
303 if (content_features(n
).light_propagates
) {
305 n
.getLight(LIGHTBANK_DAY
) == LIGHT_SUN
306 && sunlight_should_go_down
== false
308 block_below_is_valid
= false;
310 n
.getLight(LIGHTBANK_DAY
) != LIGHT_SUN
311 && sunlight_should_go_down
== true
313 block_below_is_valid
= false;
317 }catch(InvalidPositionException
&e
) {
318 // Just no block below, no need to panic.
323 return block_below_is_valid
;
327 void MapBlock::copyTo(VoxelManipulator
&dst
)
329 v3s16
data_size(MAP_BLOCKSIZE
, MAP_BLOCKSIZE
, MAP_BLOCKSIZE
);
330 VoxelArea
data_area(v3s16(0,0,0), data_size
- v3s16(1,1,1));
332 // Copy from data to VoxelManipulator
333 dst
.copyFrom(data
, data_area
, v3s16(0,0,0),
334 getPosRelative(), data_size
);
337 void MapBlock::copyFrom(VoxelManipulator
&dst
)
339 v3s16
data_size(MAP_BLOCKSIZE
, MAP_BLOCKSIZE
, MAP_BLOCKSIZE
);
340 VoxelArea
data_area(v3s16(0,0,0), data_size
- v3s16(1,1,1));
342 // Copy from VoxelManipulator to data
343 dst
.copyTo(data
, data_area
, v3s16(0,0,0),
344 getPosRelative(), data_size
);
347 void MapBlock::updateDayNightDiff()
351 m_day_night_differs
= false;
355 bool differs
= false;
358 Check if any lighting value differs
360 for(u32 i
=0; i
<MAP_BLOCKSIZE
*MAP_BLOCKSIZE
*MAP_BLOCKSIZE
; i
++)
362 MapNode
&n
= data
[i
];
363 if(n
.getLight(LIGHTBANK_DAY
) != n
.getLight(LIGHTBANK_NIGHT
))
371 If some lighting values differ, check if the whole thing is
372 just air. If it is, differ = false
376 bool only_air
= true;
377 for(u32 i
=0; i
<MAP_BLOCKSIZE
*MAP_BLOCKSIZE
*MAP_BLOCKSIZE
; i
++)
379 MapNode
&n
= data
[i
];
380 if(n
.getContent() != CONTENT_AIR
)
390 // Set member variable
391 m_day_night_differs
= differs
;
394 s16
MapBlock::getGroundLevel(v2s16 p2d
)
400 s16 y
= MAP_BLOCKSIZE
-1;
403 MapNode n
= getNodeRef(p2d
.X
, y
, p2d
.Y
);
404 if(content_features(n
).walkable
)
406 if(y
== MAP_BLOCKSIZE
-1)
414 catch(InvalidPositionException
&e
)
424 void MapBlock::serialize(std::ostream
&os
, u8 version
)
426 if (!ser_ver_supported(version
))
427 throw VersionMismatchException("ERROR: MapBlock format not supported");
430 throw SerializationError("ERROR: Not writing dummy block.");
437 if (m_day_night_differs
)
439 if (m_lighting_expired
)
441 if (m_generated
== false)
443 os
.write((char*)&flags
, 1);
445 u32 nodecount
= MAP_BLOCKSIZE
*MAP_BLOCKSIZE
*MAP_BLOCKSIZE
;
447 u32 sl
= MapNode::serializedLength(version
);
453 SharedBuffer
<u8
> databuf_nodelist(nodecount
*sl
);
454 for (u32 i
=0; i
<nodecount
; i
++) {
455 data
[i
].serialize(&databuf_nodelist
[i
*sl
], version
);
458 // Create buffer with different parameters sorted
459 SharedBuffer
<u8
> databuf(nodecount
*sl
);
460 for (u32 i
=0; i
<nodecount
; i
++) {
461 for (u32 k
=0; k
<sl
; k
++) {
462 databuf
[i
+(nodecount
*k
)] = databuf_nodelist
[(i
*sl
)+k
];
467 Compress data to output stream
470 compress(databuf
, os
, version
);
476 std::ostringstream
oss(std::ios_base::binary
);
477 m_node_metadata
.serialize(oss
);
478 compressZlib(oss
.str(), os
);
483 void MapBlock::deSerialize(std::istream
&is
, u8 version
)
485 if (!ser_ver_supported(version
))
486 throw VersionMismatchException("ERROR: MapBlock format not supported");
489 u32 nodecount
= MAP_BLOCKSIZE
*MAP_BLOCKSIZE
*MAP_BLOCKSIZE
;
492 is
.read((char*)&flags
, 1);
493 is_underground
= (flags
& 0x01) ? true : false;
494 m_day_night_differs
= (flags
& 0x02) ? true : false;
495 m_lighting_expired
= (flags
& 0x04) ? true : false;
496 m_generated
= (flags
& 0x08) ? false : true;
497 u32 sl
= MapNode::serializedLength(version
);
500 std::ostringstream
os(std::ios_base::binary
);
501 decompress(is
, os
, version
);
502 std::string s
= os
.str();
503 if (s
.size() != nodecount
*sl
)
504 throw SerializationError("MapBlock::deSerialize: decompress resulted in size"
505 " other than nodecount*nodelength");
507 // deserialize nodes from buffer
508 for (u32 i
=0; i
<nodecount
; i
++) {
509 SharedBuffer
<u8
> buf(sl
);
510 for (u32 k
=0; k
<sl
; k
++) {
511 buf
[k
] = s
[i
+(nodecount
*k
)];
513 data
[i
].deSerialize(*buf
, version
);
521 std::ostringstream
oss(std::ios_base::binary
);
522 decompressZlib(is
, oss
);
523 std::istringstream
iss(oss
.str(), std::ios_base::binary
);
524 m_node_metadata
.deSerialize(iss
);
525 }catch(SerializationError
&e
) {
526 dstream
<<"WARNING: MapBlock::deSerialize(): Ignoring an error"
527 <<" while deserializing node metadata"<<std::endl
;
532 void MapBlock::serializeDiskExtra(std::ostream
&os
, u8 version
)
534 // Versions up from 9 have block objects. (DEPRECATED)
541 // Versions up from 15 have static objects.
544 m_static_objects
.serialize(os
);
550 writeU32(os
, getTimestamp());
554 void MapBlock::deSerializeDiskExtra(std::istream
&is
, u8 version
)
557 Versions up from 9 have block objects. (DEPRECATED)
561 u16 count
= readU16(is
);
562 // Not supported and length not known if count is not 0
564 dstream
<<"WARNING: MapBlock::deSerializeDiskExtra(): "
565 <<"Ignoring stuff coming at and after MBOs"<<std::endl
;
571 Versions up from 15 have static objects.
575 m_static_objects
.deSerialize(is
);
581 setTimestamp(readU32(is
));
585 setTimestamp(BLOCK_TIMESTAMP_UNDEFINED
);
590 Get a quick string to describe what a block actually contains
592 std::string
analyze_block(MapBlock
*block
)
599 std::ostringstream desc
;
601 v3s16 p
= block
->getPos();
603 snprintf(spos
, 20, "(%2d,%2d,%2d), ", p
.X
, p
.Y
, p
.Z
);
606 switch(block
->getModified())
608 case MOD_STATE_CLEAN
:
611 case MOD_STATE_WRITE_AT_UNLOAD
:
612 desc
<<"WRITE_AT_UNLOAD, ";
614 case MOD_STATE_WRITE_NEEDED
:
615 desc
<<"WRITE_NEEDED, ";
618 desc
<<"unknown getModified()="+itos(block
->getModified())+", ";
621 if(block
->isGenerated())
622 desc
<<"is_gen [X], ";
624 desc
<<"is_gen [ ], ";
626 if(block
->getIsUnderground())
632 if(block
->getMeshExpired())
633 desc
<<"mesh_exp [X], ";
635 desc
<<"mesh_exp [ ], ";
638 if(block
->getLightingExpired())
639 desc
<<"lighting_exp [X], ";
641 desc
<<"lighting_exp [ ], ";
649 // We'll just define the numbers here, don't want to include
651 const content_t content_water
= 2;
652 const content_t content_watersource
= 9;
653 const content_t content_tree
= 0x801;
654 const content_t content_leaves
= 0x802;
655 const content_t content_jungletree
= 0x815;
657 bool full_ignore
= true;
658 bool some_ignore
= false;
659 bool full_air
= true;
660 bool some_air
= false;
663 for(s16 z0
=0; z0
<MAP_BLOCKSIZE
; z0
++)
664 for(s16 y0
=0; y0
<MAP_BLOCKSIZE
; y0
++)
665 for(s16 x0
=0; x0
<MAP_BLOCKSIZE
; x0
++)
668 MapNode n
= block
->getNodeNoEx(p
);
669 content_t c
= n
.getContent();
670 if(c
== CONTENT_IGNORE
)
678 if(c
== content_tree
|| c
== content_jungletree
679 || c
== content_leaves
)
681 if(c
== content_water
682 || c
== content_watersource
)
688 std::ostringstream ss
;
691 ss
<<"IGNORE (full), ";
705 if(ss
.str().size()>=2)
706 desc
<<ss
.str().substr(0, ss
.str().size()-2);
711 return desc
.str().substr(0, desc
.str().size()-2);