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
):
41 has_spawn_area(false),
46 m_modified(MOD_STATE_WRITE_NEEDED
),
47 is_underground(false),
48 m_lighting_expired(true),
49 m_day_night_differs(false),
51 m_timestamp(BLOCK_TIMESTAMP_UNDEFINED
),
58 //m_spawn_timer = -10000;
61 m_mesh_expired
= false;
71 JMutexAutoLock
lock(mesh_mutex
);
78 for (std::map
<v3s16
,MapBlockSound
>::iterator i
= m_sounds
.begin(); i
!= m_sounds
.end(); i
++) {
79 g_sound
->stopSound(i
->second
.id
);
89 bool MapBlock::isValidPositionParent(v3s16 p
)
91 if (isValidPosition(p
.X
,p
.Y
,p
.Z
))
93 return m_parent
->isValidPosition(getPosRelative() + p
);
96 MapNode
MapBlock::getNodeParent(v3s16 p
, bool *is_valid_position
)
98 if (isValidPosition(p
.X
,p
.Y
,p
.Z
) == false)
99 return m_parent
->getNodeNoEx(getPosRelative() + p
, is_valid_position
);
101 if (is_valid_position
)
102 *is_valid_position
= false;
103 return MapNode(CONTENT_IGNORE
);
105 if (is_valid_position
)
106 *is_valid_position
= true;
107 return data
[p
.Z
*MAP_BLOCKSIZE
*MAP_BLOCKSIZE
+ p
.Y
*MAP_BLOCKSIZE
+ p
.X
];
110 void MapBlock::setNodeParent(v3s16 p
, MapNode
& n
)
112 if (isValidPosition(p
.X
,p
.Y
,p
.Z
) == false) {
113 m_parent
->setNode(getPosRelative() + p
, n
);
115 data
[p
.Z
*MAP_BLOCKSIZE
*MAP_BLOCKSIZE
+ p
.Y
*MAP_BLOCKSIZE
+ p
.X
] = n
;
120 Propagates sunlight down through the block.
121 Doesn't modify nodes that are not affected by sunlight.
123 Returns false if sunlight at bottom block is invalid.
124 Returns true if sunlight at bottom block is valid.
125 Returns true if bottom block doesn't exist.
127 If there is a block above, continues from it.
128 If there is no block above, assumes there is sunlight, unless
129 is_underground is set or highest node is water.
131 All sunlighted nodes are added to light_sources.
133 if remove_light==true, sets non-sunlighted nodes black.
135 if black_air_left!=NULL, it is set to true if non-sunlighted
136 air is left in block.
138 bool MapBlock::propagateSunlight(core::map
<v3s16
, bool> & light_sources
,
139 bool remove_light
, bool *black_air_left
)
141 // Whether the sunlight at the top of the bottom block is valid
142 bool block_below_is_valid
= true;
144 v3s16 pos_relative
= getPosRelative();
146 for (s16 x
=0; x
<MAP_BLOCKSIZE
; x
++) {
147 for (s16 z
=0; z
<MAP_BLOCKSIZE
; z
++) {
148 bool no_sunlight
= false;
149 //bool no_top_block = false;
150 // Check if node above block has sunlight
151 bool is_valid_position
;
152 MapNode np
= getNodeParent(v3s16(x
, MAP_BLOCKSIZE
, z
), &is_valid_position
);
153 if (is_valid_position
) {
154 if (np
.getContent() == CONTENT_IGNORE
) {
156 no_sunlight
= is_underground
;
157 }else if (np
.getLight(LIGHTBANK_DAY
) != LIGHT_SUN
) {
161 //no_top_block = true;
163 // NOTE: This makes over-ground roofed places sunlighted
164 // Assume sunlight, unless is_underground==true
165 if (is_underground
) {
168 MapNode n
= getNodeNoEx(v3s16(x
, MAP_BLOCKSIZE
-1, z
));
169 //if(n.getContent() == CONTENT_WATER || n.getContent() == CONTENT_WATERSOURCE)
170 if (content_features(n
).sunlight_propagates
== false)
173 // NOTE: As of now, this just would make everything dark.
175 //no_sunlight = true;
178 s16 y
= MAP_BLOCKSIZE
-1;
180 // This makes difference to diminishing in water.
181 bool stopped_to_solid_object
= false;
183 u8 current_light
= no_sunlight
? 0 : LIGHT_SUN
;
185 for (; y
>= 0; y
--) {
187 MapNode
&n
= getNodeRef(pos
);
188 ContentFeatures
&f
= content_features(n
);
190 if (current_light
!= 0 && (current_light
!= LIGHT_SUN
|| !f
.sunlight_propagates
)) {
191 if (!f
.light_propagates
) {
192 // A solid object is on the way.
193 stopped_to_solid_object
= true;
199 current_light
= diminish_light(current_light
);
203 u8 old_light
= n
.getLight(LIGHTBANK_DAY
);
205 if (current_light
> old_light
|| remove_light
)
206 n
.setLight(LIGHTBANK_DAY
, current_light
);
208 if (diminish_light(current_light
) != 0)
209 light_sources
.insert(pos_relative
+ pos
, true);
211 if (current_light
== 0 && stopped_to_solid_object
&& black_air_left
)
212 *black_air_left
= true;
215 // Whether or not the block below should see LIGHT_SUN
216 bool sunlight_should_go_down
= (current_light
== LIGHT_SUN
);
219 If the block below hasn't already been marked invalid:
221 Check if the node below the block has proper sunlight at top.
222 If not, the block below is invalid.
224 Ignore non-transparent nodes as they always have no light
227 if (block_below_is_valid
) {
228 MapNode n
= getNodeParent(v3s16(x
, -1, z
));
229 if (content_features(n
).light_propagates
) {
231 n
.getLight(LIGHTBANK_DAY
) == LIGHT_SUN
232 && sunlight_should_go_down
== false
234 block_below_is_valid
= false;
236 n
.getLight(LIGHTBANK_DAY
) != LIGHT_SUN
237 && sunlight_should_go_down
== true
239 block_below_is_valid
= false;
243 }catch(InvalidPositionException
&e
) {
244 // Just no block below, no need to panic.
249 return block_below_is_valid
;
253 void MapBlock::copyTo(VoxelManipulator
&dst
)
255 v3s16
data_size(MAP_BLOCKSIZE
, MAP_BLOCKSIZE
, MAP_BLOCKSIZE
);
256 VoxelArea
data_area(v3s16(0,0,0), data_size
- v3s16(1,1,1));
258 // Copy from data to VoxelManipulator
259 dst
.copyFrom(data
, data_area
, v3s16(0,0,0),
260 getPosRelative(), data_size
);
263 void MapBlock::copyFrom(VoxelManipulator
&dst
)
265 v3s16
data_size(MAP_BLOCKSIZE
, MAP_BLOCKSIZE
, MAP_BLOCKSIZE
);
266 VoxelArea
data_area(v3s16(0,0,0), data_size
- v3s16(1,1,1));
268 // Copy from VoxelManipulator to data
269 dst
.copyTo(data
, data_area
, v3s16(0,0,0),
270 getPosRelative(), data_size
);
273 void MapBlock::updateDayNightDiff()
277 m_day_night_differs
= false;
281 bool differs
= false;
284 Check if any lighting value differs
286 for(u32 i
=0; i
<MAP_BLOCKSIZE
*MAP_BLOCKSIZE
*MAP_BLOCKSIZE
; i
++)
288 MapNode
&n
= data
[i
];
289 if(n
.getLight(LIGHTBANK_DAY
) != n
.getLight(LIGHTBANK_NIGHT
))
297 If some lighting values differ, check if the whole thing is
298 just air. If it is, differ = false
302 bool only_air
= true;
303 for(u32 i
=0; i
<MAP_BLOCKSIZE
*MAP_BLOCKSIZE
*MAP_BLOCKSIZE
; i
++)
305 MapNode
&n
= data
[i
];
306 if(n
.getContent() != CONTENT_AIR
)
316 // Set member variable
317 m_day_night_differs
= differs
;
320 s16
MapBlock::getGroundLevel(v2s16 p2d
)
326 s16 y
= MAP_BLOCKSIZE
-1;
329 MapNode n
= getNodeRef(p2d
.X
, y
, p2d
.Y
);
330 if(content_features(n
).walkable
)
332 if(y
== MAP_BLOCKSIZE
-1)
340 catch(InvalidPositionException
&e
)
350 void MapBlock::serialize(std::ostream
&os
, u8 version
)
352 if (!ser_ver_supported(version
))
353 throw VersionMismatchException("ERROR: MapBlock format not supported");
356 throw SerializationError("ERROR: Not writing dummy block.");
363 if (m_day_night_differs
)
365 if (m_lighting_expired
)
367 if (m_generated
== false)
369 os
.write((char*)&flags
, 1);
371 u32 nodecount
= MAP_BLOCKSIZE
*MAP_BLOCKSIZE
*MAP_BLOCKSIZE
;
373 u32 sl
= MapNode::serializedLength(version
);
379 SharedBuffer
<u8
> databuf_nodelist(nodecount
*sl
);
380 for (u32 i
=0; i
<nodecount
; i
++) {
381 data
[i
].serialize(&databuf_nodelist
[i
*sl
], version
);
384 // Create buffer with different parameters sorted
385 SharedBuffer
<u8
> databuf(nodecount
*sl
);
386 for (u32 i
=0; i
<nodecount
; i
++) {
387 for (u32 k
=0; k
<sl
; k
++) {
388 databuf
[i
+(nodecount
*k
)] = databuf_nodelist
[(i
*sl
)+k
];
393 Compress data to output stream
396 compress(databuf
, os
, version
);
402 std::ostringstream
oss(std::ios_base::binary
);
403 m_node_metadata
.serialize(oss
);
404 compressZlib(oss
.str(), os
);
409 void MapBlock::deSerialize(std::istream
&is
, u8 version
)
411 if (!ser_ver_supported(version
))
412 throw VersionMismatchException("ERROR: MapBlock format not supported");
415 u32 nodecount
= MAP_BLOCKSIZE
*MAP_BLOCKSIZE
*MAP_BLOCKSIZE
;
418 is
.read((char*)&flags
, 1);
419 is_underground
= (flags
& 0x01) ? true : false;
420 m_day_night_differs
= (flags
& 0x02) ? true : false;
421 m_lighting_expired
= (flags
& 0x04) ? true : false;
422 m_generated
= (flags
& 0x08) ? false : true;
423 u32 sl
= MapNode::serializedLength(version
);
426 std::ostringstream
os(std::ios_base::binary
);
427 decompress(is
, os
, version
);
428 std::string s
= os
.str();
429 if (s
.size() != nodecount
*sl
)
430 throw SerializationError("MapBlock::deSerialize: decompress resulted in size"
431 " other than nodecount*nodelength");
433 // deserialize nodes from buffer
434 for (u32 i
=0; i
<nodecount
; i
++) {
435 SharedBuffer
<u8
> buf(sl
);
436 for (u32 k
=0; k
<sl
; k
++) {
437 buf
[k
] = s
[i
+(nodecount
*k
)];
439 data
[i
].deSerialize(*buf
, version
);
447 std::ostringstream
oss(std::ios_base::binary
);
448 decompressZlib(is
, oss
);
449 std::istringstream
iss(oss
.str(), std::ios_base::binary
);
450 m_node_metadata
.deSerialize(iss
);
451 }catch(SerializationError
&e
) {
452 dstream
<<"WARNING: MapBlock::deSerialize(): Ignoring an error"
453 <<" while deserializing node metadata"<<std::endl
;
458 void MapBlock::serializeDiskExtra(std::ostream
&os
, u8 version
)
460 // Versions up from 9 have block objects. (DEPRECATED)
467 // Versions up from 15 have static objects.
470 m_static_objects
.serialize(os
);
476 writeU32(os
, getTimestamp());
480 void MapBlock::deSerializeDiskExtra(std::istream
&is
, u8 version
)
483 Versions up from 9 have block objects. (DEPRECATED)
487 u16 count
= readU16(is
);
488 // Not supported and length not known if count is not 0
490 dstream
<<"WARNING: MapBlock::deSerializeDiskExtra(): "
491 <<"Ignoring stuff coming at and after MBOs"<<std::endl
;
497 Versions up from 15 have static objects.
501 m_static_objects
.deSerialize(is
);
507 setTimestamp(readU32(is
));
511 setTimestamp(BLOCK_TIMESTAMP_UNDEFINED
);
516 Get a quick string to describe what a block actually contains
518 std::string
analyze_block(MapBlock
*block
)
525 std::ostringstream desc
;
527 v3s16 p
= block
->getPos();
529 snprintf(spos
, 20, "(%2d,%2d,%2d), ", p
.X
, p
.Y
, p
.Z
);
532 switch(block
->getModified())
534 case MOD_STATE_CLEAN
:
537 case MOD_STATE_WRITE_AT_UNLOAD
:
538 desc
<<"WRITE_AT_UNLOAD, ";
540 case MOD_STATE_WRITE_NEEDED
:
541 desc
<<"WRITE_NEEDED, ";
544 desc
<<"unknown getModified()="+itos(block
->getModified())+", ";
547 if(block
->isGenerated())
548 desc
<<"is_gen [X], ";
550 desc
<<"is_gen [ ], ";
552 if(block
->getIsUnderground())
558 if(block
->getMeshExpired())
559 desc
<<"mesh_exp [X], ";
561 desc
<<"mesh_exp [ ], ";
564 if(block
->getLightingExpired())
565 desc
<<"lighting_exp [X], ";
567 desc
<<"lighting_exp [ ], ";
575 // We'll just define the numbers here, don't want to include
577 const content_t content_water
= 2;
578 const content_t content_watersource
= 9;
579 const content_t content_tree
= 0x801;
580 const content_t content_leaves
= 0x802;
581 const content_t content_jungletree
= 0x815;
583 bool full_ignore
= true;
584 bool some_ignore
= false;
585 bool full_air
= true;
586 bool some_air
= false;
589 for(s16 z0
=0; z0
<MAP_BLOCKSIZE
; z0
++)
590 for(s16 y0
=0; y0
<MAP_BLOCKSIZE
; y0
++)
591 for(s16 x0
=0; x0
<MAP_BLOCKSIZE
; x0
++)
594 MapNode n
= block
->getNodeNoEx(p
);
595 content_t c
= n
.getContent();
596 if(c
== CONTENT_IGNORE
)
604 if(c
== content_tree
|| c
== content_jungletree
605 || c
== content_leaves
)
607 if(c
== content_water
608 || c
== content_watersource
)
614 std::ostringstream ss
;
617 ss
<<"IGNORE (full), ";
631 if(ss
.str().size()>=2)
632 desc
<<ss
.str().substr(0, ss
.str().size()-2);
637 return desc
.str().substr(0, desc
.str().size()-2);