This is 1608.01
[voxelands/voxelands-menche.git] / src / mapblock.cpp
blob531eb03dd95bb147f00b5d4e7cc1136aa7632321
1 /************************************************************************
2 * Minetest-c55
3 * Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
5 * mapblock.cpp
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
23 * for Voxelands.
24 ************************************************************************/
26 #include "mapblock.h"
27 #include "map.h"
28 // For g_settings
29 #include "main.h"
30 #include "light.h"
31 #include <sstream>
32 #ifndef SERVER
33 #include "sound.h"
34 #endif
37 MapBlock
40 MapBlock::MapBlock(Map *parent, v3s16 pos, bool dummy):
41 has_spawn_area(false),
42 spawn_area(0,0,0),
43 last_spawn(0),
44 m_parent(parent),
45 m_pos(pos),
46 m_modified(MOD_STATE_WRITE_NEEDED),
47 is_underground(false),
48 m_lighting_expired(true),
49 m_day_night_differs(false),
50 m_generated(false),
51 m_timestamp(BLOCK_TIMESTAMP_UNDEFINED),
52 m_usage_timer(0)
54 data = NULL;
55 if (dummy == false)
56 reallocate();
58 //m_spawn_timer = -10000;
60 #ifndef SERVER
61 m_mesh_expired = false;
62 mesh_mutex.Init();
63 mesh = NULL;
64 #endif
67 MapBlock::~MapBlock()
69 #ifndef SERVER
71 JMutexAutoLock lock(mesh_mutex);
73 if (mesh) {
74 delete mesh;
75 mesh = NULL;
77 if (g_sound) {
78 for (std::map<v3s16,MapBlockSound>::iterator i = m_sounds.begin(); i != m_sounds.end(); i++) {
79 g_sound->stopSound(i->second.id);
83 #endif
85 if (data)
86 delete[] data;
89 bool MapBlock::isValidPositionParent(v3s16 p)
91 if (isValidPosition(p.X,p.Y,p.Z))
92 return true;
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);
100 if (data == NULL) {
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);
114 }else{
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) {
155 // Trust heuristics
156 no_sunlight = is_underground;
157 }else if (np.getLight(LIGHTBANK_DAY) != LIGHT_SUN) {
158 no_sunlight = true;
160 }else{
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) {
166 no_sunlight = true;
167 }else{
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)
171 no_sunlight = true;
173 // NOTE: As of now, this just would make everything dark.
174 // No sunlight here
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--) {
186 v3s16 pos(x, y, z);
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;
195 // Light stops.
196 current_light = 0;
197 }else{
198 // Diminish light
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
226 try {
227 if (block_below_is_valid) {
228 MapNode n = getNodeParent(v3s16(x, -1, z));
229 if (content_features(n).light_propagates) {
230 if (
231 n.getLight(LIGHTBANK_DAY) == LIGHT_SUN
232 && sunlight_should_go_down == false
234 block_below_is_valid = false;
235 }else if (
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()
275 if(data == NULL)
277 m_day_night_differs = false;
278 return;
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))
291 differs = true;
292 break;
297 If some lighting values differ, check if the whole thing is
298 just air. If it is, differ = false
300 if(differs)
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)
308 only_air = false;
309 break;
312 if(only_air)
313 differs = false;
316 // Set member variable
317 m_day_night_differs = differs;
320 s16 MapBlock::getGroundLevel(v2s16 p2d)
322 if(isDummy())
323 return -3;
326 s16 y = MAP_BLOCKSIZE-1;
327 for(; y>=0; y--)
329 MapNode n = getNodeRef(p2d.X, y, p2d.Y);
330 if(content_features(n).walkable)
332 if(y == MAP_BLOCKSIZE-1)
333 return -2;
334 else
335 return y;
338 return -1;
340 catch(InvalidPositionException &e)
342 return -3;
347 Serialization
350 void MapBlock::serialize(std::ostream &os, u8 version)
352 if (!ser_ver_supported(version))
353 throw VersionMismatchException("ERROR: MapBlock format not supported");
355 if (data == NULL)
356 throw SerializationError("ERROR: Not writing dummy block.");
359 // First byte
360 u8 flags = 0;
361 if (is_underground)
362 flags |= 0x01;
363 if (m_day_night_differs)
364 flags |= 0x02;
365 if (m_lighting_expired)
366 flags |= 0x04;
367 if (m_generated == false)
368 flags |= 0x08;
369 os.write((char*)&flags, 1);
371 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
373 u32 sl = MapNode::serializedLength(version);
376 Get data
378 // Serialize nodes
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);
399 NodeMetadata
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;
417 u8 flags;
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);
425 // Uncompress data
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);
443 NodeMetadata
445 // Ignore errors
446 try{
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)
461 if(version >= 9)
463 // count=0
464 writeU16(os, 0);
467 // Versions up from 15 have static objects.
468 if(version >= 15)
470 m_static_objects.serialize(os);
473 // Timestamp
474 if(version >= 17)
476 writeU32(os, getTimestamp());
480 void MapBlock::deSerializeDiskExtra(std::istream &is, u8 version)
483 Versions up from 9 have block objects. (DEPRECATED)
485 if(version >= 9)
487 u16 count = readU16(is);
488 // Not supported and length not known if count is not 0
489 if(count != 0){
490 dstream<<"WARNING: MapBlock::deSerializeDiskExtra(): "
491 <<"Ignoring stuff coming at and after MBOs"<<std::endl;
492 return;
497 Versions up from 15 have static objects.
499 if(version >= 15)
501 m_static_objects.deSerialize(is);
504 // Timestamp
505 if(version >= 17)
507 setTimestamp(readU32(is));
509 else
511 setTimestamp(BLOCK_TIMESTAMP_UNDEFINED);
516 Get a quick string to describe what a block actually contains
518 std::string analyze_block(MapBlock *block)
520 if(block == NULL)
522 return "NULL";
525 std::ostringstream desc;
527 v3s16 p = block->getPos();
528 char spos[20];
529 snprintf(spos, 20, "(%2d,%2d,%2d), ", p.X, p.Y, p.Z);
530 desc<<spos;
532 switch(block->getModified())
534 case MOD_STATE_CLEAN:
535 desc<<"CLEAN, ";
536 break;
537 case MOD_STATE_WRITE_AT_UNLOAD:
538 desc<<"WRITE_AT_UNLOAD, ";
539 break;
540 case MOD_STATE_WRITE_NEEDED:
541 desc<<"WRITE_NEEDED, ";
542 break;
543 default:
544 desc<<"unknown getModified()="+itos(block->getModified())+", ";
547 if(block->isGenerated())
548 desc<<"is_gen [X], ";
549 else
550 desc<<"is_gen [ ], ";
552 if(block->getIsUnderground())
553 desc<<"is_ug [X], ";
554 else
555 desc<<"is_ug [ ], ";
557 #ifndef SERVER
558 if(block->getMeshExpired())
559 desc<<"mesh_exp [X], ";
560 else
561 desc<<"mesh_exp [ ], ";
562 #endif
564 if(block->getLightingExpired())
565 desc<<"lighting_exp [X], ";
566 else
567 desc<<"lighting_exp [ ], ";
569 if(block->isDummy())
571 desc<<"Dummy, ";
573 else
575 // We'll just define the numbers here, don't want to include
576 // content_mapnode.h
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;
587 bool trees = false;
588 bool water = 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++)
593 v3s16 p(x0,y0,z0);
594 MapNode n = block->getNodeNoEx(p);
595 content_t c = n.getContent();
596 if(c == CONTENT_IGNORE)
597 some_ignore = true;
598 else
599 full_ignore = false;
600 if(c == CONTENT_AIR)
601 some_air = true;
602 else
603 full_air = false;
604 if(c == content_tree || c == content_jungletree
605 || c == content_leaves)
606 trees = true;
607 if(c == content_water
608 || c == content_watersource)
609 water = true;
612 desc<<"content {";
614 std::ostringstream ss;
616 if(full_ignore)
617 ss<<"IGNORE (full), ";
618 else if(some_ignore)
619 ss<<"IGNORE, ";
621 if(full_air)
622 ss<<"AIR (full), ";
623 else if(some_air)
624 ss<<"AIR, ";
626 if(trees)
627 ss<<"trees, ";
628 if(water)
629 ss<<"water, ";
631 if(ss.str().size()>=2)
632 desc<<ss.str().substr(0, ss.str().size()-2);
634 desc<<"}, ";
637 return desc.str().substr(0, desc.str().size()-2);
641 //END