1505.01
[voxelands/voxelands-menche.git] / src / mapblock.cpp
blobf985791df779bba43c52088a968a54b92fb103d8
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 m_parent(parent),
42 m_pos(pos),
43 m_modified(MOD_STATE_WRITE_NEEDED),
44 is_underground(false),
45 m_lighting_expired(true),
46 m_day_night_differs(false),
47 m_generated(false),
48 m_timestamp(BLOCK_TIMESTAMP_UNDEFINED),
49 m_usage_timer(0)
51 data = NULL;
52 if(dummy == false)
53 reallocate();
55 //m_spawn_timer = -10000;
57 #ifndef SERVER
58 m_mesh_expired = false;
59 mesh_mutex.Init();
60 mesh = NULL;
61 m_temp_mods_mutex.Init();
62 #endif
65 MapBlock::~MapBlock()
67 #ifndef SERVER
69 JMutexAutoLock lock(mesh_mutex);
71 if (mesh) {
72 delete mesh;
73 mesh = NULL;
75 if (g_sound) {
76 for (std::map<v3s16,MapBlockSound>::iterator i = m_sounds.begin(); i != m_sounds.end(); i++) {
77 g_sound->stopSound(i->second.id);
81 #endif
83 if (data)
84 delete[] data;
87 bool MapBlock::isValidPositionParent(v3s16 p)
89 if (isValidPosition(p.X,p.Y,p.Z))
90 return true;
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);
98 if (data == NULL) {
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);
112 }else{
113 data[p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X] = n;
117 #ifndef SERVER
119 #if 1
120 void MapBlock::updateMesh(u32 daynight_ratio, Environment *env, v3s16 camera_offset)
122 #if 0
124 DEBUG: If mesh has been generated, don't generate it again
127 JMutexAutoLock meshlock(mesh_mutex);
128 if(mesh != NULL)
129 return;
131 #endif
133 MeshMakeData data;
134 data.m_env = env;
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);
143 Replace the mesh
146 replaceMesh(mesh_new);
149 #endif
151 void MapBlock::replaceMesh(MapBlockMesh *mesh_new)
153 mesh_mutex.Lock();
155 //scene::SMesh *mesh_old = mesh[daynight_i];
156 //mesh[daynight_i] = mesh_new;
158 MapBlockMesh *mesh_old = mesh;
159 mesh = mesh_new;
160 setMeshExpired(false);
162 if(mesh_old != NULL)
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;
182 // Drop the mesh
183 //mesh_old->drop();
185 delete mesh_old;
188 mesh_mutex.Unlock();
191 #endif // !SERVER
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) {
229 // Trust heuristics
230 no_sunlight = is_underground;
231 }else if (np.getLight(LIGHTBANK_DAY) != LIGHT_SUN) {
232 no_sunlight = true;
234 }else{
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) {
240 no_sunlight = true;
241 }else{
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)
245 no_sunlight = true;
247 // NOTE: As of now, this just would make everything dark.
248 // No sunlight here
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--) {
260 v3s16 pos(x, y, z);
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;
269 // Light stops.
270 current_light = 0;
271 }else{
272 // Diminish light
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
300 try {
301 if (block_below_is_valid) {
302 MapNode n = getNodeParent(v3s16(x, -1, z));
303 if (content_features(n).light_propagates) {
304 if (
305 n.getLight(LIGHTBANK_DAY) == LIGHT_SUN
306 && sunlight_should_go_down == false
308 block_below_is_valid = false;
309 }else if (
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()
349 if(data == NULL)
351 m_day_night_differs = false;
352 return;
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))
365 differs = true;
366 break;
371 If some lighting values differ, check if the whole thing is
372 just air. If it is, differ = false
374 if(differs)
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)
382 only_air = false;
383 break;
386 if(only_air)
387 differs = false;
390 // Set member variable
391 m_day_night_differs = differs;
394 s16 MapBlock::getGroundLevel(v2s16 p2d)
396 if(isDummy())
397 return -3;
400 s16 y = MAP_BLOCKSIZE-1;
401 for(; y>=0; y--)
403 MapNode n = getNodeRef(p2d.X, y, p2d.Y);
404 if(content_features(n).walkable)
406 if(y == MAP_BLOCKSIZE-1)
407 return -2;
408 else
409 return y;
412 return -1;
414 catch(InvalidPositionException &e)
416 return -3;
421 Serialization
424 void MapBlock::serialize(std::ostream &os, u8 version)
426 if (!ser_ver_supported(version))
427 throw VersionMismatchException("ERROR: MapBlock format not supported");
429 if (data == NULL)
430 throw SerializationError("ERROR: Not writing dummy block.");
433 // First byte
434 u8 flags = 0;
435 if (is_underground)
436 flags |= 0x01;
437 if (m_day_night_differs)
438 flags |= 0x02;
439 if (m_lighting_expired)
440 flags |= 0x04;
441 if (m_generated == false)
442 flags |= 0x08;
443 os.write((char*)&flags, 1);
445 u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
447 u32 sl = MapNode::serializedLength(version);
450 Get data
452 // Serialize nodes
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);
473 NodeMetadata
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;
491 u8 flags;
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);
499 // Uncompress data
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);
517 NodeMetadata
519 // Ignore errors
520 try{
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)
535 if(version >= 9)
537 // count=0
538 writeU16(os, 0);
541 // Versions up from 15 have static objects.
542 if(version >= 15)
544 m_static_objects.serialize(os);
547 // Timestamp
548 if(version >= 17)
550 writeU32(os, getTimestamp());
554 void MapBlock::deSerializeDiskExtra(std::istream &is, u8 version)
557 Versions up from 9 have block objects. (DEPRECATED)
559 if(version >= 9)
561 u16 count = readU16(is);
562 // Not supported and length not known if count is not 0
563 if(count != 0){
564 dstream<<"WARNING: MapBlock::deSerializeDiskExtra(): "
565 <<"Ignoring stuff coming at and after MBOs"<<std::endl;
566 return;
571 Versions up from 15 have static objects.
573 if(version >= 15)
575 m_static_objects.deSerialize(is);
578 // Timestamp
579 if(version >= 17)
581 setTimestamp(readU32(is));
583 else
585 setTimestamp(BLOCK_TIMESTAMP_UNDEFINED);
590 Get a quick string to describe what a block actually contains
592 std::string analyze_block(MapBlock *block)
594 if(block == NULL)
596 return "NULL";
599 std::ostringstream desc;
601 v3s16 p = block->getPos();
602 char spos[20];
603 snprintf(spos, 20, "(%2d,%2d,%2d), ", p.X, p.Y, p.Z);
604 desc<<spos;
606 switch(block->getModified())
608 case MOD_STATE_CLEAN:
609 desc<<"CLEAN, ";
610 break;
611 case MOD_STATE_WRITE_AT_UNLOAD:
612 desc<<"WRITE_AT_UNLOAD, ";
613 break;
614 case MOD_STATE_WRITE_NEEDED:
615 desc<<"WRITE_NEEDED, ";
616 break;
617 default:
618 desc<<"unknown getModified()="+itos(block->getModified())+", ";
621 if(block->isGenerated())
622 desc<<"is_gen [X], ";
623 else
624 desc<<"is_gen [ ], ";
626 if(block->getIsUnderground())
627 desc<<"is_ug [X], ";
628 else
629 desc<<"is_ug [ ], ";
631 #ifndef SERVER
632 if(block->getMeshExpired())
633 desc<<"mesh_exp [X], ";
634 else
635 desc<<"mesh_exp [ ], ";
636 #endif
638 if(block->getLightingExpired())
639 desc<<"lighting_exp [X], ";
640 else
641 desc<<"lighting_exp [ ], ";
643 if(block->isDummy())
645 desc<<"Dummy, ";
647 else
649 // We'll just define the numbers here, don't want to include
650 // content_mapnode.h
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;
661 bool trees = false;
662 bool water = 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++)
667 v3s16 p(x0,y0,z0);
668 MapNode n = block->getNodeNoEx(p);
669 content_t c = n.getContent();
670 if(c == CONTENT_IGNORE)
671 some_ignore = true;
672 else
673 full_ignore = false;
674 if(c == CONTENT_AIR)
675 some_air = true;
676 else
677 full_air = false;
678 if(c == content_tree || c == content_jungletree
679 || c == content_leaves)
680 trees = true;
681 if(c == content_water
682 || c == content_watersource)
683 water = true;
686 desc<<"content {";
688 std::ostringstream ss;
690 if(full_ignore)
691 ss<<"IGNORE (full), ";
692 else if(some_ignore)
693 ss<<"IGNORE, ";
695 if(full_air)
696 ss<<"AIR (full), ";
697 else if(some_air)
698 ss<<"AIR, ";
700 if(trees)
701 ss<<"trees, ";
702 if(water)
703 ss<<"water, ";
705 if(ss.str().size()>=2)
706 desc<<ss.str().substr(0, ss.str().size()-2);
708 desc<<"}, ";
711 return desc.str().substr(0, desc.str().size()-2);
715 //END