2010-11-27 00:02:21 +01:00
/*
2013-02-24 18:40:43 +01:00
Minetest
2013-02-24 19:38:45 +01:00
Copyright ( C ) 2010 - 2013 celeron55 , Perttu Ahola < celeron55 @ gmail . com >
2010-11-29 19:13:04 +01:00
This program is free software ; you can redistribute it and / or modify
2012-06-05 16:56:56 +02:00
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation ; either version 2.1 of the License , or
2010-11-29 19:13:04 +01:00
( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
2012-06-05 16:56:56 +02:00
GNU Lesser General Public License for more details .
2010-11-29 19:13:04 +01:00
2012-06-05 16:56:56 +02:00
You should have received a copy of the GNU Lesser General Public License along
2010-11-29 19:13:04 +01:00
with this program ; if not , write to the Free Software Foundation , Inc . ,
51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 USA .
2010-11-27 00:02:21 +01:00
*/
# include "map.h"
2011-06-25 23:03:58 +02:00
# include "mapsector.h"
# include "mapblock.h"
2010-11-27 00:02:21 +01:00
# include "main.h"
# include "filesys.h"
2010-11-29 09:52:07 +01:00
# include "voxel.h"
2010-12-22 02:33:58 +01:00
# include "porting.h"
2011-06-25 17:12:41 +02:00
# include "mapgen.h"
2011-06-26 01:34:36 +02:00
# include "nodemetadata.h"
2011-10-12 12:53:38 +02:00
# include "settings.h"
2011-10-16 13:57:53 +02:00
# include "log.h"
2011-10-17 16:06:28 +02:00
# include "profiler.h"
2011-11-14 20:41:30 +01:00
# include "nodedef.h"
# include "gamedef.h"
2012-06-17 01:40:36 +02:00
# include "util/directiontables.h"
2012-07-26 21:06:45 +02:00
# include "rollback_interface.h"
2013-02-14 04:43:15 +01:00
# include "emerge.h"
2013-01-23 04:32:30 +01:00
# include "mapgen_v6.h"
2010-11-27 00:02:21 +01:00
2011-10-17 00:04:22 +02:00
# define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
2011-06-25 03:25:14 +02:00
/*
SQLite format specification :
- Initially only replaces sectors / and sectors2 /
2012-12-22 06:34:35 +01:00
2011-09-03 01:07:14 +02:00
If map . sqlite does not exist in the save dir
or the block was not found in the database
the map will try to load from sectors folder .
In either case , map . sqlite will be created
and all future saves will save there .
2012-12-22 06:34:35 +01:00
2011-09-03 01:07:14 +02:00
Structure of map . sqlite :
Tables :
blocks
( PK ) INT pos
BLOB data
2011-06-25 03:25:14 +02:00
*/
2010-11-29 09:52:07 +01:00
/*
Map
*/
2011-11-13 23:19:48 +01:00
Map : : Map ( std : : ostream & dout , IGameDef * gamedef ) :
2010-11-27 00:02:21 +01:00
m_dout ( dout ) ,
2011-11-13 23:19:48 +01:00
m_gamedef ( gamedef ) ,
2011-02-05 13:55:16 +01:00
m_sector_cache ( NULL )
2010-11-27 00:02:21 +01:00
{
2011-04-21 18:35:17 +02:00
/*m_sector_mutex.Init();
assert ( m_sector_mutex . IsInitialized ( ) ) ; */
2010-11-27 00:02:21 +01:00
}
Map : : ~ Map ( )
{
/*
2011-02-23 01:49:57 +01:00
Free all MapSectors
2010-11-27 00:02:21 +01:00
*/
core : : map < v2s16 , MapSector * > : : Iterator i = m_sectors . getIterator ( ) ;
for ( ; i . atEnd ( ) = = false ; i + + )
{
MapSector * sector = i . getNode ( ) - > getValue ( ) ;
delete sector ;
}
}
2011-02-23 01:49:57 +01:00
void Map : : addEventReceiver ( MapEventReceiver * event_receiver )
{
m_event_receivers . insert ( event_receiver , false ) ;
}
void Map : : removeEventReceiver ( MapEventReceiver * event_receiver )
{
if ( m_event_receivers . find ( event_receiver ) = = NULL )
return ;
m_event_receivers . remove ( event_receiver ) ;
}
void Map : : dispatchEvent ( MapEditEvent * event )
{
for ( core : : map < MapEventReceiver * , bool > : : Iterator
i = m_event_receivers . getIterator ( ) ;
i . atEnd ( ) = = false ; i + + )
{
MapEventReceiver * event_receiver = i . getNode ( ) - > getKey ( ) ;
event_receiver - > onMapEditEvent ( event ) ;
}
}
2011-01-30 00:44:54 +01:00
MapSector * Map : : getSectorNoGenerateNoExNoLock ( v2s16 p )
2010-11-27 00:02:21 +01:00
{
if ( m_sector_cache ! = NULL & & p = = m_sector_cache_p ) {
MapSector * sector = m_sector_cache ;
return sector ;
}
2012-12-22 06:34:35 +01:00
2010-11-27 00:02:21 +01:00
core : : map < v2s16 , MapSector * > : : Node * n = m_sectors . find ( p ) ;
2012-12-22 06:34:35 +01:00
2010-11-27 00:02:21 +01:00
if ( n = = NULL )
2011-01-30 00:44:54 +01:00
return NULL ;
2012-12-22 06:34:35 +01:00
2010-11-27 00:02:21 +01:00
MapSector * sector = n - > getValue ( ) ;
2012-12-22 06:34:35 +01:00
2010-11-27 00:02:21 +01:00
// Cache the last result
m_sector_cache_p = p ;
m_sector_cache = sector ;
return sector ;
}
2011-01-30 00:44:54 +01:00
MapSector * Map : : getSectorNoGenerateNoEx ( v2s16 p )
{
return getSectorNoGenerateNoExNoLock ( p ) ;
}
MapSector * Map : : getSectorNoGenerate ( v2s16 p )
{
MapSector * sector = getSectorNoGenerateNoEx ( p ) ;
if ( sector = = NULL )
throw InvalidPositionException ( ) ;
2012-12-22 06:34:35 +01:00
2011-01-30 00:44:54 +01:00
return sector ;
}
2010-12-21 17:08:24 +01:00
MapBlock * Map : : getBlockNoCreateNoEx ( v3s16 p3d )
{
2010-11-27 00:02:21 +01:00
v2s16 p2d ( p3d . X , p3d . Z ) ;
2011-06-26 00:31:43 +02:00
MapSector * sector = getSectorNoGenerateNoEx ( p2d ) ;
if ( sector = = NULL )
2010-12-21 17:08:24 +01:00
return NULL ;
2011-06-25 23:03:58 +02:00
MapBlock * block = sector - > getBlockNoCreateNoEx ( p3d . Y ) ;
2010-11-27 00:02:21 +01:00
return block ;
2010-12-21 17:08:24 +01:00
}
2011-06-25 23:03:58 +02:00
MapBlock * Map : : getBlockNoCreate ( v3s16 p3d )
2012-12-22 06:34:35 +01:00
{
2011-06-25 23:03:58 +02:00
MapBlock * block = getBlockNoCreateNoEx ( p3d ) ;
if ( block = = NULL )
throw InvalidPositionException ( ) ;
2011-02-01 02:06:02 +01:00
return block ;
2010-12-21 17:08:24 +01:00
}
2011-02-01 02:06:02 +01:00
2010-11-27 00:02:21 +01:00
bool Map : : isNodeUnderground ( v3s16 p )
{
v3s16 blockpos = getNodeBlockPos ( p ) ;
try {
MapBlock * block = getBlockNoCreate ( blockpos ) ;
return block - > getIsUnderground ( ) ;
}
catch ( InvalidPositionException & e )
{
return false ;
}
}
2011-06-26 01:34:36 +02:00
bool Map : : isValidPosition ( v3s16 p )
{
v3s16 blockpos = getNodeBlockPos ( p ) ;
MapBlock * block = getBlockNoCreate ( blockpos ) ;
return ( block ! = NULL ) ;
}
// Returns a CONTENT_IGNORE node if not found
MapNode Map : : getNodeNoEx ( v3s16 p )
{
v3s16 blockpos = getNodeBlockPos ( p ) ;
MapBlock * block = getBlockNoCreateNoEx ( blockpos ) ;
if ( block = = NULL )
return MapNode ( CONTENT_IGNORE ) ;
v3s16 relpos = p - blockpos * MAP_BLOCKSIZE ;
return block - > getNodeNoCheck ( relpos ) ;
}
// throws InvalidPositionException if not found
MapNode Map : : getNode ( v3s16 p )
{
v3s16 blockpos = getNodeBlockPos ( p ) ;
MapBlock * block = getBlockNoCreateNoEx ( blockpos ) ;
if ( block = = NULL )
throw InvalidPositionException ( ) ;
v3s16 relpos = p - blockpos * MAP_BLOCKSIZE ;
return block - > getNodeNoCheck ( relpos ) ;
}
// throws InvalidPositionException if not found
void Map : : setNode ( v3s16 p , MapNode & n )
{
v3s16 blockpos = getNodeBlockPos ( p ) ;
MapBlock * block = getBlockNoCreate ( blockpos ) ;
v3s16 relpos = p - blockpos * MAP_BLOCKSIZE ;
2011-11-25 16:42:41 +01:00
// Never allow placing CONTENT_IGNORE, it fucks up stuff
if ( n . getContent ( ) = = CONTENT_IGNORE ) {
errorstream < < " Map::setNode(): Not allowing to place CONTENT_IGNORE "
< < " while trying to replace \" "
< < m_gamedef - > ndef ( ) - > get ( block - > getNodeNoCheck ( relpos ) ) . name
< < " \" at " < < PP ( p ) < < " (block " < < PP ( blockpos ) < < " ) " < < std : : endl ;
2011-11-29 20:05:36 +01:00
debug_stacks_print_to ( infostream ) ;
2011-11-25 16:42:41 +01:00
return ;
}
2011-06-26 01:34:36 +02:00
block - > setNodeNoCheck ( relpos , n ) ;
}
2010-11-27 00:02:21 +01:00
/*
Goes recursively through the neighbours of the node .
Alters only transparent nodes .
If the lighting of the neighbour is lower than the lighting of
the node was ( before changing it to 0 at the step before ) , the
lighting of the neighbour is set to 0 and then the same stuff
repeats for the neighbour .
The ending nodes of the routine are stored in light_sources .
This is useful when a light is removed . In such case , this
routine can be called for the light node and then again for
light_sources to re - light the area without the removed light .
values of from_nodes are lighting values .
*/
2010-12-18 16:46:00 +01:00
void Map : : unspreadLight ( enum LightBank bank ,
core : : map < v3s16 , u8 > & from_nodes ,
2010-11-27 00:02:21 +01:00
core : : map < v3s16 , bool > & light_sources ,
core : : map < v3s16 , MapBlock * > & modified_blocks )
{
2011-11-14 20:41:30 +01:00
INodeDefManager * nodemgr = m_gamedef - > ndef ( ) ;
2010-11-27 00:02:21 +01:00
v3s16 dirs [ 6 ] = {
v3s16 ( 0 , 0 , 1 ) , // back
v3s16 ( 0 , 1 , 0 ) , // top
v3s16 ( 1 , 0 , 0 ) , // right
v3s16 ( 0 , 0 , - 1 ) , // front
v3s16 ( 0 , - 1 , 0 ) , // bottom
v3s16 ( - 1 , 0 , 0 ) , // left
} ;
2012-12-22 06:34:35 +01:00
2010-11-27 00:02:21 +01:00
if ( from_nodes . size ( ) = = 0 )
return ;
2012-12-22 06:34:35 +01:00
2010-11-27 00:02:21 +01:00
u32 blockchangecount = 0 ;
core : : map < v3s16 , u8 > unlighted_nodes ;
core : : map < v3s16 , u8 > : : Iterator j ;
j = from_nodes . getIterator ( ) ;
/*
Initialize block cache
*/
v3s16 blockpos_last ;
MapBlock * block = NULL ;
// Cache this a bit, too
bool block_checked_in_modified = false ;
2012-12-22 06:34:35 +01:00
2010-11-27 00:02:21 +01:00
for ( ; j . atEnd ( ) = = false ; j + + )
{
v3s16 pos = j . getNode ( ) - > getKey ( ) ;
v3s16 blockpos = getNodeBlockPos ( pos ) ;
2012-12-22 06:34:35 +01:00
2010-11-27 00:02:21 +01:00
// Only fetch a new block if the block position has changed
try {
if ( block = = NULL | | blockpos ! = blockpos_last ) {
block = getBlockNoCreate ( blockpos ) ;
blockpos_last = blockpos ;
block_checked_in_modified = false ;
blockchangecount + + ;
}
}
catch ( InvalidPositionException & e )
{
continue ;
}
if ( block - > isDummy ( ) )
continue ;
// Calculate relative position in block
v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE ;
// Get node straight from the block
MapNode n = block - > getNode ( relpos ) ;
2011-06-25 03:25:14 +02:00
2010-11-27 00:02:21 +01:00
u8 oldlight = j . getNode ( ) - > getValue ( ) ;
2011-06-25 03:25:14 +02:00
2010-11-27 00:02:21 +01:00
// Loop through 6 neighbors
for ( u16 i = 0 ; i < 6 ; i + + )
{
// Get the position of the neighbor node
v3s16 n2pos = pos + dirs [ i ] ;
2011-06-25 03:25:14 +02:00
2010-11-27 00:02:21 +01:00
// Get the block where the node is located
v3s16 blockpos = getNodeBlockPos ( n2pos ) ;
try
{
// Only fetch a new block if the block position has changed
try {
if ( block = = NULL | | blockpos ! = blockpos_last ) {
block = getBlockNoCreate ( blockpos ) ;
blockpos_last = blockpos ;
block_checked_in_modified = false ;
blockchangecount + + ;
}
}
catch ( InvalidPositionException & e )
{
continue ;
}
2011-06-25 03:25:14 +02:00
2010-11-27 00:02:21 +01:00
// Calculate relative position in block
v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE ;
// Get node straight from the block
MapNode n2 = block - > getNode ( relpos ) ;
2011-06-25 03:25:14 +02:00
2010-11-27 00:02:21 +01:00
bool changed = false ;
//TODO: Optimize output by optimizing light_sources?
/*
If the neighbor is dimmer than what was specified
as oldlight ( the light of the previous node )
*/
2011-11-14 20:41:30 +01:00
if ( n2 . getLight ( bank , nodemgr ) < oldlight )
2010-11-27 00:02:21 +01:00
{
/*
And the neighbor is transparent and it has some light
*/
2011-11-14 20:41:30 +01:00
if ( nodemgr - > get ( n2 ) . light_propagates
& & n2 . getLight ( bank , nodemgr ) ! = 0 )
2010-11-27 00:02:21 +01:00
{
/*
Set light to 0 and add to queue
*/
2011-11-14 20:41:30 +01:00
u8 current_light = n2 . getLight ( bank , nodemgr ) ;
n2 . setLight ( bank , 0 , nodemgr ) ;
2010-11-27 00:02:21 +01:00
block - > setNode ( relpos , n2 ) ;
unlighted_nodes . insert ( n2pos , current_light ) ;
changed = true ;
/*
Remove from light_sources if it is there
NOTE : This doesn ' t happen nearly at all
*/
/*if(light_sources.find(n2pos))
{
2011-10-16 13:57:53 +02:00
infostream < < " Removed from light_sources " < < std : : endl ;
2010-11-27 00:02:21 +01:00
light_sources . remove ( n2pos ) ;
} */
}
2011-06-25 03:25:14 +02:00
2010-12-13 02:19:12 +01:00
/*// DEBUG
if ( light_sources . find ( n2pos ) ! = NULL )
light_sources . remove ( n2pos ) ; */
2010-11-27 00:02:21 +01:00
}
else {
light_sources . insert ( n2pos , true ) ;
}
// Add to modified_blocks
if ( changed = = true & & block_checked_in_modified = = false )
{
// If the block is not found in modified_blocks, add.
if ( modified_blocks . find ( blockpos ) = = NULL )
{
modified_blocks . insert ( blockpos , block ) ;
}
block_checked_in_modified = true ;
}
}
catch ( InvalidPositionException & e )
{
continue ;
}
}
}
2011-10-16 13:57:53 +02:00
/*infostream<<"unspreadLight(): Changed block "
2010-11-27 00:02:21 +01:00
< < blockchangecount < < " times "
< < " for " < < from_nodes . size ( ) < < " nodes "
< < std : : endl ; */
2011-06-25 03:25:14 +02:00
2010-11-27 00:02:21 +01:00
if ( unlighted_nodes . size ( ) > 0 )
2010-12-18 16:46:00 +01:00
unspreadLight ( bank , unlighted_nodes , light_sources , modified_blocks ) ;
2010-11-27 00:02:21 +01:00
}
/*
A single - node wrapper of the above
*/
2010-12-18 16:46:00 +01:00
void Map : : unLightNeighbors ( enum LightBank bank ,
v3s16 pos , u8 lightwas ,
2010-11-27 00:02:21 +01:00
core : : map < v3s16 , bool > & light_sources ,
core : : map < v3s16 , MapBlock * > & modified_blocks )
{
core : : map < v3s16 , u8 > from_nodes ;
from_nodes . insert ( pos , lightwas ) ;
2010-12-18 16:46:00 +01:00
unspreadLight ( bank , from_nodes , light_sources , modified_blocks ) ;
2010-11-27 00:02:21 +01:00
}
/*
Lights neighbors of from_nodes , collects all them and then
goes on recursively .
*/
2010-12-18 16:46:00 +01:00
void Map : : spreadLight ( enum LightBank bank ,
core : : map < v3s16 , bool > & from_nodes ,
2010-11-27 00:02:21 +01:00
core : : map < v3s16 , MapBlock * > & modified_blocks )
{
2011-11-14 20:41:30 +01:00
INodeDefManager * nodemgr = m_gamedef - > ndef ( ) ;
2010-11-27 00:02:21 +01:00
const v3s16 dirs [ 6 ] = {
v3s16 ( 0 , 0 , 1 ) , // back
v3s16 ( 0 , 1 , 0 ) , // top
v3s16 ( 1 , 0 , 0 ) , // right
v3s16 ( 0 , 0 , - 1 ) , // front
v3s16 ( 0 , - 1 , 0 ) , // bottom
v3s16 ( - 1 , 0 , 0 ) , // left
} ;
if ( from_nodes . size ( ) = = 0 )
return ;
2011-06-25 03:25:14 +02:00
2010-11-27 00:02:21 +01:00
u32 blockchangecount = 0 ;
core : : map < v3s16 , bool > lighted_nodes ;
core : : map < v3s16 , bool > : : Iterator j ;
j = from_nodes . getIterator ( ) ;
/*
Initialize block cache
*/
v3s16 blockpos_last ;
MapBlock * block = NULL ;
// Cache this a bit, too
bool block_checked_in_modified = false ;
2011-06-25 03:25:14 +02:00
2010-11-27 00:02:21 +01:00
for ( ; j . atEnd ( ) = = false ; j + + )
//for(; j != from_nodes.end(); j++)
{
v3s16 pos = j . getNode ( ) - > getKey ( ) ;
//v3s16 pos = *j;
2011-10-16 13:57:53 +02:00
//infostream<<"pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"<<std::endl;
2010-11-27 00:02:21 +01:00
v3s16 blockpos = getNodeBlockPos ( pos ) ;
2011-06-25 03:25:14 +02:00
2010-11-27 00:02:21 +01:00
// Only fetch a new block if the block position has changed
try {
if ( block = = NULL | | blockpos ! = blockpos_last ) {
block = getBlockNoCreate ( blockpos ) ;
blockpos_last = blockpos ;
block_checked_in_modified = false ;
blockchangecount + + ;
}
}
catch ( InvalidPositionException & e )
{
continue ;
}
if ( block - > isDummy ( ) )
continue ;
// Calculate relative position in block
v3s16 relpos = pos - blockpos_last * MAP_BLOCKSIZE ;
// Get node straight from the block
MapNode n = block - > getNode ( relpos ) ;
2011-11-14 20:41:30 +01:00
u8 oldlight = n . getLight ( bank , nodemgr ) ;
2010-11-27 00:02:21 +01:00
u8 newlight = diminish_light ( oldlight ) ;
// Loop through 6 neighbors
for ( u16 i = 0 ; i < 6 ; i + + ) {
// Get the position of the neighbor node
v3s16 n2pos = pos + dirs [ i ] ;
2011-06-25 03:25:14 +02:00
2010-11-27 00:02:21 +01:00
// Get the block where the node is located
v3s16 blockpos = getNodeBlockPos ( n2pos ) ;
try
{
// Only fetch a new block if the block position has changed
try {
if ( block = = NULL | | blockpos ! = blockpos_last ) {
block = getBlockNoCreate ( blockpos ) ;
blockpos_last = blockpos ;
block_checked_in_modified = false ;
blockchangecount + + ;
}
}
catch ( InvalidPositionException & e )
{
continue ;
}
2011-06-25 03:25:14 +02:00
2010-11-27 00:02:21 +01:00
// Calculate relative position in block
v3s16 relpos = n2pos - blockpos * MAP_BLOCKSIZE ;
// Get node straight from the block
MapNode n2 = block - > getNode ( relpos ) ;
2011-06-25 03:25:14 +02:00
2010-11-27 00:02:21 +01:00
bool changed = false ;
/*
If the neighbor is brighter than the current node ,
add to list ( it will light up this node on its turn )
*/
2011-11-14 20:41:30 +01:00
if ( n2 . getLight ( bank , nodemgr ) > undiminish_light ( oldlight ) )
2010-11-27 00:02:21 +01:00
{
lighted_nodes . insert ( n2pos , true ) ;
//lighted_nodes.push_back(n2pos);
changed = true ;
}
/*
If the neighbor is dimmer than how much light this node
would spread on it , add to list
*/
2011-11-14 20:41:30 +01:00
if ( n2 . getLight ( bank , nodemgr ) < newlight )
2010-11-27 00:02:21 +01:00
{
2011-11-14 20:41:30 +01:00
if ( nodemgr - > get ( n2 ) . light_propagates )
2010-11-27 00:02:21 +01:00
{
2011-11-14 20:41:30 +01:00
n2 . setLight ( bank , newlight , nodemgr ) ;
2010-11-27 00:02:21 +01:00
block - > setNode ( relpos , n2 ) ;
lighted_nodes . insert ( n2pos , true ) ;
//lighted_nodes.push_back(n2pos);
changed = true ;
}
}
// Add to modified_blocks
if ( changed = = true & & block_checked_in_modified = = false )
{
// If the block is not found in modified_blocks, add.
if ( modified_blocks . find ( blockpos ) = = NULL )
{
modified_blocks . insert ( blockpos , block ) ;
}
block_checked_in_modified = true ;
}
}
catch ( InvalidPositionException & e )
{
continue ;
}
}
}
2011-10-16 13:57:53 +02:00
/*infostream<<"spreadLight(): Changed block "
2010-11-27 00:02:21 +01:00
< < blockchangecount < < " times "
< < " for " < < from_nodes . size ( ) < < " nodes "
< < std : : endl ; */
2011-06-25 03:25:14 +02:00
2010-11-27 00:02:21 +01:00
if ( lighted_nodes . size ( ) > 0 )
2010-12-18 16:46:00 +01:00
spreadLight ( bank , lighted_nodes , modified_blocks ) ;
2010-11-27 00:02:21 +01:00
}
/*
A single - node source variation of the above .
*/
2010-12-18 16:46:00 +01:00
void Map : : lightNeighbors ( enum LightBank bank ,
v3s16 pos ,
2010-11-27 00:02:21 +01:00
core : : map < v3s16 , MapBlock * > & modified_blocks )
{
core : : map < v3s16 , bool > from_nodes ;
from_nodes . insert ( pos , true ) ;
2010-12-18 16:46:00 +01:00
spreadLight ( bank , from_nodes , modified_blocks ) ;
2010-11-27 00:02:21 +01:00
}
2010-12-18 16:46:00 +01:00
v3s16 Map : : getBrightestNeighbour ( enum LightBank bank , v3s16 p )
2010-11-27 00:02:21 +01:00
{
2011-11-14 20:41:30 +01:00
INodeDefManager * nodemgr = m_gamedef - > ndef ( ) ;
2010-11-27 00:02:21 +01:00
v3s16 dirs [ 6 ] = {
v3s16 ( 0 , 0 , 1 ) , // back
v3s16 ( 0 , 1 , 0 ) , // top
v3s16 ( 1 , 0 , 0 ) , // right
v3s16 ( 0 , 0 , - 1 ) , // front
v3s16 ( 0 , - 1 , 0 ) , // bottom
v3s16 ( - 1 , 0 , 0 ) , // left
} ;
2011-06-25 03:25:14 +02:00
2010-11-27 00:02:21 +01:00
u8 brightest_light = 0 ;
v3s16 brightest_pos ( 0 , 0 , 0 ) ;
bool found_something = false ;
// Loop through 6 neighbors
for ( u16 i = 0 ; i < 6 ; i + + ) {
// Get the position of the neighbor node
v3s16 n2pos = p + dirs [ i ] ;
MapNode n2 ;
try {
n2 = getNode ( n2pos ) ;
}
catch ( InvalidPositionException & e )
{
continue ;
}
2011-11-14 20:41:30 +01:00
if ( n2 . getLight ( bank , nodemgr ) > brightest_light | | found_something = = false ) {
brightest_light = n2 . getLight ( bank , nodemgr ) ;
2010-11-27 00:02:21 +01:00
brightest_pos = n2pos ;
found_something = true ;
}
}
if ( found_something = = false )
throw InvalidPositionException ( ) ;
2011-06-25 03:25:14 +02:00
2010-11-27 00:02:21 +01:00
return brightest_pos ;
}
/*
Propagates sunlight down from a node .
Starting point gets sunlight .
Returns the lowest y value of where the sunlight went .
2010-12-26 13:34:34 +01:00
Mud is turned into grass in where the sunlight stops .
2010-11-27 00:02:21 +01:00
*/
s16 Map : : propagateSunlight ( v3s16 start ,
core : : map < v3s16 , MapBlock * > & modified_blocks )
{
2011-11-14 20:41:30 +01:00
INodeDefManager * nodemgr = m_gamedef - > ndef ( ) ;
2010-11-27 00:02:21 +01:00
s16 y = start . Y ;
for ( ; ; y - - )
{
v3s16 pos ( start . X , y , start . Z ) ;
2011-06-25 03:25:14 +02:00
2010-11-27 00:02:21 +01:00
v3s16 blockpos = getNodeBlockPos ( pos ) ;
MapBlock * block ;
try {
block = getBlockNoCreate ( blockpos ) ;
}
catch ( InvalidPositionException & e )
{
break ;
}
v3s16 relpos = pos - blockpos * MAP_BLOCKSIZE ;
MapNode n = block - > getNode ( relpos ) ;
2011-11-14 20:41:30 +01:00
if ( nodemgr - > get ( n ) . sunlight_propagates )
2010-11-27 00:02:21 +01:00
{
2011-11-14 20:41:30 +01:00
n . setLight ( LIGHTBANK_DAY , LIGHT_SUN , nodemgr ) ;
2010-11-27 00:02:21 +01:00
block - > setNode ( relpos , n ) ;
modified_blocks . insert ( blockpos , block ) ;
}
2010-12-26 13:34:34 +01:00
else
{
// Sunlight goes no further
2010-11-27 00:02:21 +01:00
break ;
}
}
return y + 1 ;
}
2010-12-18 16:46:00 +01:00
void Map : : updateLighting ( enum LightBank bank ,
core : : map < v3s16 , MapBlock * > & a_blocks ,
2010-11-27 00:02:21 +01:00
core : : map < v3s16 , MapBlock * > & modified_blocks )
{
2011-11-14 20:41:30 +01:00
INodeDefManager * nodemgr = m_gamedef - > ndef ( ) ;
2010-11-27 00:02:21 +01:00
/*m_dout<<DTIME<<"Map::updateLighting(): "
2011-01-24 15:36:58 +01:00
< < a_blocks . size ( ) < < " blocks. " < < std : : endl ; */
2011-06-25 03:25:14 +02:00
2011-01-24 15:36:58 +01:00
//TimeTaker timer("updateLighting");
2011-06-25 03:25:14 +02:00
2010-11-27 00:02:21 +01:00
// For debugging
2011-01-25 23:41:06 +01:00
//bool debug=true;
//u32 count_was = modified_blocks.size();
2011-06-25 03:25:14 +02:00
2011-01-30 00:44:54 +01:00
core : : map < v3s16 , MapBlock * > blocks_to_update ;
2010-11-27 00:02:21 +01:00
core : : map < v3s16 , bool > light_sources ;
2011-06-25 03:25:14 +02:00
2010-11-27 00:02:21 +01:00
core : : map < v3s16 , u8 > unlight_from ;
2012-01-27 12:24:06 +01:00
int num_bottom_invalid = 0 ;
2012-12-22 06:34:35 +01:00
2012-01-27 00:40:57 +01:00
{
2012-01-27 13:53:54 +01:00
//TimeTaker t("first stuff");
2011-06-25 03:25:14 +02:00
2010-11-27 00:02:21 +01:00
core : : map < v3s16 , MapBlock * > : : Iterator i ;
i = a_blocks . getIterator ( ) ;
for ( ; i . atEnd ( ) = = false ; i + + )
{
MapBlock * block = i . getNode ( ) - > getValue ( ) ;
2011-06-25 03:25:14 +02:00
2010-11-27 00:02:21 +01:00
for ( ; ; )
{
// Don't bother with dummy blocks.
if ( block - > isDummy ( ) )
break ;
2011-06-25 03:25:14 +02:00
2010-11-27 00:02:21 +01:00
v3s16 pos = block - > getPos ( ) ;
2012-01-27 00:40:57 +01:00
v3s16 posnodes = block - > getPosRelative ( ) ;
2010-11-27 00:02:21 +01:00
modified_blocks . insert ( pos , block ) ;
2011-01-30 00:44:54 +01:00
blocks_to_update . insert ( pos , block ) ;
2010-11-27 00:02:21 +01:00
/*
Clear all light from block
*/
for ( s16 z = 0 ; z < MAP_BLOCKSIZE ; z + + )
for ( s16 x = 0 ; x < MAP_BLOCKSIZE ; x + + )
for ( s16 y = 0 ; y < MAP_BLOCKSIZE ; y + + )
{
2011-06-25 03:25:14 +02:00
2010-11-27 00:02:21 +01:00
try {
v3s16 p ( x , y , z ) ;
2012-01-27 00:40:57 +01:00
MapNode n = block - > getNode ( p ) ;
2011-11-14 20:41:30 +01:00
u8 oldlight = n . getLight ( bank , nodemgr ) ;
n . setLight ( bank , 0 , nodemgr ) ;
2012-01-27 00:40:57 +01:00
block - > setNode ( p , n ) ;
2011-06-25 03:25:14 +02:00
2012-01-27 03:02:42 +01:00
// If node sources light, add to list
u8 source = nodemgr - > get ( n ) . light_source ;
if ( source ! = 0 )
light_sources [ p + posnodes ] = true ;
2010-11-27 00:02:21 +01:00
// Collect borders for unlighting
2012-01-27 03:02:42 +01:00
if ( ( x = = 0 | | x = = MAP_BLOCKSIZE - 1
2010-12-13 02:19:12 +01:00
| | y = = 0 | | y = = MAP_BLOCKSIZE - 1
| | z = = 0 | | z = = MAP_BLOCKSIZE - 1 )
2012-01-27 03:02:42 +01:00
& & oldlight ! = 0 )
2010-11-27 00:02:21 +01:00
{
2012-01-27 00:40:57 +01:00
v3s16 p_map = p + posnodes ;
2010-11-27 00:02:21 +01:00
unlight_from . insert ( p_map , oldlight ) ;
}
}
catch ( InvalidPositionException & e )
{
/*
This would happen when dealing with a
dummy block .
*/
//assert(0);
2011-10-16 13:57:53 +02:00
infostream < < " updateLighting(): InvalidPositionException "
2010-11-27 00:02:21 +01:00
< < std : : endl ;
}
}
2011-06-25 03:25:14 +02:00
2010-12-18 16:46:00 +01:00
if ( bank = = LIGHTBANK_DAY )
{
bool bottom_valid = block - > propagateSunlight ( light_sources ) ;
2010-11-27 00:02:21 +01:00
2012-01-27 12:24:06 +01:00
if ( ! bottom_valid )
num_bottom_invalid + + ;
2010-12-18 16:46:00 +01:00
// If bottom is valid, we're done.
if ( bottom_valid )
break ;
}
else if ( bank = = LIGHTBANK_NIGHT )
{
2011-01-30 00:44:54 +01:00
// For night lighting, sunlight is not propagated
2010-11-27 00:02:21 +01:00
break ;
2010-12-18 16:46:00 +01:00
}
else
{
2011-01-30 00:44:54 +01:00
// Invalid lighting bank
2010-12-18 16:46:00 +01:00
assert ( 0 ) ;
}
2011-06-25 03:25:14 +02:00
2011-10-16 13:57:53 +02:00
/*infostream<<"Bottom for sunlight-propagated block ("
2010-11-27 00:02:21 +01:00
< < pos . X < < " , " < < pos . Y < < " , " < < pos . Z < < " ) not valid "
< < std : : endl ; */
2011-01-30 00:44:54 +01:00
// Bottom sunlight is not valid; get the block and loop to it
2010-11-27 00:02:21 +01:00
pos . Y - - ;
try {
block = getBlockNoCreate ( pos ) ;
}
catch ( InvalidPositionException & e )
{
assert ( 0 ) ;
}
2011-06-25 03:25:14 +02:00
2010-11-27 00:02:21 +01:00
}
}
2012-01-27 00:40:57 +01:00
}
2012-01-27 12:24:06 +01:00
2011-06-26 17:51:57 +02:00
/*
Enable this to disable proper lighting for speeding up map
generation for testing or whatever
*/
#if 0
2011-10-12 12:53:38 +02:00
//if(g_settings->get(""))
2011-06-26 17:51:57 +02:00
{
core : : map < v3s16 , MapBlock * > : : Iterator i ;
i = blocks_to_update . getIterator ( ) ;
for ( ; i . atEnd ( ) = = false ; i + + )
{
MapBlock * block = i . getNode ( ) - > getValue ( ) ;
v3s16 p = block - > getPos ( ) ;
block - > setLightingExpired ( false ) ;
}
return ;
}
# endif
2011-01-24 15:36:58 +01:00
2012-01-27 03:02:42 +01:00
# if 1
2010-11-27 00:02:21 +01:00
{
2012-01-27 03:02:42 +01:00
//TimeTaker timer("unspreadLight");
2010-12-18 16:46:00 +01:00
unspreadLight ( bank , unlight_from , light_sources , modified_blocks ) ;
2010-11-27 00:02:21 +01:00
}
2011-06-25 03:25:14 +02:00
2012-01-27 03:02:42 +01:00
/*if(debug)
2010-11-27 00:02:21 +01:00
{
u32 diff = modified_blocks . size ( ) - count_was ;
count_was = modified_blocks . size ( ) ;
2011-10-16 13:57:53 +02:00
infostream < < " unspreadLight modified " < < diff < < std : : endl ;
2012-01-27 03:02:42 +01:00
} */
2010-11-27 00:02:21 +01:00
{
2012-01-27 03:02:42 +01:00
//TimeTaker timer("spreadLight");
2010-12-18 16:46:00 +01:00
spreadLight ( bank , light_sources , modified_blocks ) ;
2010-11-27 00:02:21 +01:00
}
2011-06-25 03:25:14 +02:00
2012-01-27 03:02:42 +01:00
/*if(debug)
2010-11-27 00:02:21 +01:00
{
u32 diff = modified_blocks . size ( ) - count_was ;
count_was = modified_blocks . size ( ) ;
2011-10-16 13:57:53 +02:00
infostream < < " spreadLight modified " < < diff < < std : : endl ;
2012-01-27 03:02:42 +01:00
} */
2011-01-24 15:36:58 +01:00
# endif
2011-06-25 03:25:14 +02:00
2012-01-27 03:02:42 +01:00
#if 0
2011-01-24 15:36:58 +01:00
{
//MapVoxelManipulator vmanip(this);
2012-12-22 06:34:35 +01:00
2011-01-30 00:44:54 +01:00
// Make a manual voxel manipulator and load all the blocks
// that touch the requested blocks
ManualMapVoxelManipulator vmanip ( this ) ;
2012-01-27 03:02:42 +01:00
{
//TimeTaker timer("initialEmerge");
2011-01-24 15:36:58 +01:00
core : : map < v3s16 , MapBlock * > : : Iterator i ;
2011-01-30 00:44:54 +01:00
i = blocks_to_update . getIterator ( ) ;
2011-01-24 15:36:58 +01:00
for ( ; i . atEnd ( ) = = false ; i + + )
{
MapBlock * block = i . getNode ( ) - > getValue ( ) ;
v3s16 p = block - > getPos ( ) ;
2011-06-25 03:25:14 +02:00
2011-01-30 00:44:54 +01:00
// Add all surrounding blocks
2011-01-24 15:36:58 +01:00
vmanip . initialEmerge ( p - v3s16 ( 1 , 1 , 1 ) , p + v3s16 ( 1 , 1 , 1 ) ) ;
2011-01-30 00:44:54 +01:00
/*
Add all surrounding blocks that have up - to - date lighting
NOTE : This doesn ' t quite do the job ( not everything
2011-06-25 03:25:14 +02:00
appropriate is lighted )
2011-01-30 00:44:54 +01:00
*/
/*for(s16 z=-1; z<=1; z++)
for ( s16 y = - 1 ; y < = 1 ; y + + )
for ( s16 x = - 1 ; x < = 1 ; x + + )
{
2011-11-27 11:07:22 +01:00
v3s16 p2 = p + v3s16 ( x , y , z ) ;
MapBlock * block = getBlockNoCreateNoEx ( p2 ) ;
2011-01-30 00:44:54 +01:00
if ( block = = NULL )
continue ;
if ( block - > isDummy ( ) )
continue ;
if ( block - > getLightingExpired ( ) )
continue ;
2011-11-27 11:07:22 +01:00
vmanip . initialEmerge ( p2 , p2 ) ;
2011-01-30 00:44:54 +01:00
} */
2011-06-25 03:25:14 +02:00
2011-01-30 00:44:54 +01:00
// Lighting of block will be updated completely
block - > setLightingExpired ( false ) ;
2011-01-24 15:36:58 +01:00
}
2012-01-27 03:02:42 +01:00
}
2011-01-30 00:44:54 +01:00
2011-01-24 15:36:58 +01:00
{
2012-01-27 12:24:06 +01:00
//TimeTaker timer("unSpreadLight");
2011-11-14 20:41:30 +01:00
vmanip . unspreadLight ( bank , unlight_from , light_sources , nodemgr ) ;
2011-01-24 15:36:58 +01:00
}
{
2012-01-27 12:24:06 +01:00
//TimeTaker timer("spreadLight");
2011-11-14 20:41:30 +01:00
vmanip . spreadLight ( bank , light_sources , nodemgr ) ;
2011-01-24 15:36:58 +01:00
}
{
2012-01-27 12:24:06 +01:00
//TimeTaker timer("blitBack");
2011-01-24 15:36:58 +01:00
vmanip . blitBack ( modified_blocks ) ;
}
2011-10-16 13:57:53 +02:00
/*infostream<<"emerge_time="<<emerge_time<<std::endl;
2011-01-24 15:36:58 +01:00
emerge_time = 0 ; */
}
2012-01-27 03:02:42 +01:00
# endif
2010-11-27 00:02:21 +01:00
//m_dout<<"Done ("<<getTimestamp()<<")"<<std::endl;
}
2010-12-18 16:46:00 +01:00
void Map : : updateLighting ( core : : map < v3s16 , MapBlock * > & a_blocks ,
core : : map < v3s16 , MapBlock * > & modified_blocks )
{
updateLighting ( LIGHTBANK_DAY , a_blocks , modified_blocks ) ;
updateLighting ( LIGHTBANK_NIGHT , a_blocks , modified_blocks ) ;
2011-06-25 03:25:14 +02:00
2010-12-19 15:51:45 +01:00
/*
Update information about whether day and night light differ
*/
for ( core : : map < v3s16 , MapBlock * > : : Iterator
i = modified_blocks . getIterator ( ) ;
i . atEnd ( ) = = false ; i + + )
{
MapBlock * block = i . getNode ( ) - > getValue ( ) ;
2012-03-28 23:28:48 +02:00
block - > expireDayNightDiff ( ) ;
2010-12-19 15:51:45 +01:00
}
2010-12-18 16:46:00 +01:00
}
2010-11-27 00:02:21 +01:00
/*
*/
void Map : : addNodeAndUpdate ( v3s16 p , MapNode n ,
2012-01-21 00:11:44 +01:00
core : : map < v3s16 , MapBlock * > & modified_blocks )
2010-11-27 00:02:21 +01:00
{
2012-07-26 21:06:45 +02:00
INodeDefManager * ndef = m_gamedef - > ndef ( ) ;
2011-11-14 20:41:30 +01:00
2010-11-27 00:02:21 +01:00
/*PrintInfo(m_dout);
2011-02-15 11:33:59 +01:00
m_dout < < DTIME < < " Map::addNodeAndUpdate(): p=( "
2010-11-27 00:02:21 +01:00
< < p . X < < " , " < < p . Y < < " , " < < p . Z < < " ) " < < std : : endl ; */
2012-12-22 06:34:35 +01:00
2010-11-27 00:02:21 +01:00
/*
From this node to nodes underneath :
If lighting is sunlight ( 1.0 ) , unlight neighbours and
set lighting to 0.
Else discontinue .
*/
v3s16 toppos = p + v3s16 ( 0 , 1 , 0 ) ;
2010-12-21 17:08:24 +01:00
v3s16 bottompos = p + v3s16 ( 0 , - 1 , 0 ) ;
2010-11-27 00:02:21 +01:00
2010-12-18 16:46:00 +01:00
bool node_under_sunlight = true ;
core : : map < v3s16 , bool > light_sources ;
2012-12-22 06:34:35 +01:00
2012-07-26 21:06:45 +02:00
/*
Collect old node for rollback
*/
RollbackNode rollback_oldnode ( this , p , m_gamedef ) ;
2010-12-18 16:46:00 +01:00
2010-11-27 00:02:21 +01:00
/*
If there is a node at top and it doesn ' t have sunlight ,
there has not been any sunlight going down .
Otherwise there probably is .
*/
try {
MapNode topnode = getNode ( toppos ) ;
2012-07-26 21:06:45 +02:00
if ( topnode . getLight ( LIGHTBANK_DAY , ndef ) ! = LIGHT_SUN )
2010-11-27 00:02:21 +01:00
node_under_sunlight = false ;
}
catch ( InvalidPositionException & e )
{
}
2011-05-22 16:00:09 +02:00
2011-02-15 11:33:59 +01:00
/*
Remove all light that has come out of this node
*/
2010-12-18 16:46:00 +01:00
enum LightBank banks [ ] =
{
LIGHTBANK_DAY ,
LIGHTBANK_NIGHT
} ;
for ( s32 i = 0 ; i < 2 ; i + + )
{
enum LightBank bank = banks [ i ] ;
2012-07-26 21:06:45 +02:00
u8 lightwas = getNode ( p ) . getLight ( bank , ndef ) ;
2010-12-18 16:46:00 +01:00
// Add the block of the added node to modified_blocks
v3s16 blockpos = getNodeBlockPos ( p ) ;
MapBlock * block = getBlockNoCreate ( blockpos ) ;
assert ( block ! = NULL ) ;
modified_blocks . insert ( blockpos , block ) ;
2011-06-25 03:25:14 +02:00
2011-02-23 01:49:57 +01:00
assert ( isValidPosition ( p ) ) ;
2011-06-25 03:25:14 +02:00
2010-12-18 16:46:00 +01:00
// Unlight neighbours of node.
// This means setting light of all consequent dimmer nodes
// to 0.
// This also collects the nodes at the border which will spread
// light again into this.
unLightNeighbors ( bank , p , lightwas , light_sources , modified_blocks ) ;
2012-07-26 21:06:45 +02:00
n . setLight ( bank , 0 , ndef ) ;
2010-12-18 16:46:00 +01:00
}
2011-02-15 11:33:59 +01:00
2011-04-03 15:21:06 +02:00
/*
If node lets sunlight through and is under sunlight , it has
sunlight too .
*/
2012-07-26 21:06:45 +02:00
if ( node_under_sunlight & & ndef - > get ( n ) . sunlight_propagates )
2011-04-03 15:21:06 +02:00
{
2012-07-26 21:06:45 +02:00
n . setLight ( LIGHTBANK_DAY , LIGHT_SUN , ndef ) ;
2011-04-03 15:21:06 +02:00
}
2011-06-25 03:25:14 +02:00
2012-06-02 10:01:28 +02:00
/*
Remove node metadata
*/
removeNodeMetadata ( p ) ;
2011-02-15 11:33:59 +01:00
/*
Set the node on the map
*/
2011-06-25 03:25:14 +02:00
2010-11-27 00:02:21 +01:00
setNode ( p , n ) ;
2011-04-04 02:45:08 +02:00
2010-11-27 00:02:21 +01:00
/*
2011-04-03 15:21:06 +02:00
If node is under sunlight and doesn ' t let sunlight through ,
take all sunlighted nodes under it and clear light from them
and from where the light has been spread .
2010-12-18 16:46:00 +01:00
TODO : This could be optimized by mass - unlighting instead
2011-06-25 03:25:14 +02:00
of looping
2010-11-27 00:02:21 +01:00
*/
2012-07-26 21:06:45 +02:00
if ( node_under_sunlight & & ! ndef - > get ( n ) . sunlight_propagates )
2010-11-27 00:02:21 +01:00
{
s16 y = p . Y - 1 ;
for ( ; ; y - - ) {
//m_dout<<DTIME<<"y="<<y<<std::endl;
v3s16 n2pos ( p . X , y , p . Z ) ;
2011-06-25 03:25:14 +02:00
2010-11-27 00:02:21 +01:00
MapNode n2 ;
try {
n2 = getNode ( n2pos ) ;
}
catch ( InvalidPositionException & e )
{
break ;
}
2012-07-26 21:06:45 +02:00
if ( n2 . getLight ( LIGHTBANK_DAY , ndef ) = = LIGHT_SUN )
2010-11-27 00:02:21 +01:00
{
2010-12-18 16:46:00 +01:00
unLightNeighbors ( LIGHTBANK_DAY ,
2012-07-26 21:06:45 +02:00
n2pos , n2 . getLight ( LIGHTBANK_DAY , ndef ) ,
2010-12-18 16:46:00 +01:00
light_sources , modified_blocks ) ;
2012-07-26 21:06:45 +02:00
n2 . setLight ( LIGHTBANK_DAY , 0 , ndef ) ;
2010-11-27 00:02:21 +01:00
setNode ( n2pos , n2 ) ;
}
else
break ;
}
}
2011-04-03 15:21:06 +02:00
2010-12-18 16:46:00 +01:00
for ( s32 i = 0 ; i < 2 ; i + + )
{
enum LightBank bank = banks [ i ] ;
2011-06-25 03:25:14 +02:00
2010-12-18 16:46:00 +01:00
/*
Spread light from all nodes that might be capable of doing so
*/
spreadLight ( bank , light_sources , modified_blocks ) ;
}
2010-12-19 15:51:45 +01:00
/*
Update information about whether day and night light differ
*/
for ( core : : map < v3s16 , MapBlock * > : : Iterator
i = modified_blocks . getIterator ( ) ;
i . atEnd ( ) = = false ; i + + )
{
MapBlock * block = i . getNode ( ) - > getValue ( ) ;
2012-03-28 23:28:48 +02:00
block - > expireDayNightDiff ( ) ;
2010-12-19 15:51:45 +01:00
}
2011-01-17 20:15:31 +01:00
2012-07-26 21:06:45 +02:00
/*
Report for rollback
*/
if ( m_gamedef - > rollback ( ) )
{
RollbackNode rollback_newnode ( this , p , m_gamedef ) ;
RollbackAction action ;
action . setSetNode ( p , rollback_oldnode , rollback_newnode ) ;
m_gamedef - > rollback ( ) - > reportAction ( action ) ;
}
2011-01-17 20:15:31 +01:00
/*
Add neighboring liquid nodes and the node itself if it is
liquid ( = water node was added ) to transform queue .
*/
v3s16 dirs [ 7 ] = {
v3s16 ( 0 , 0 , 0 ) , // self
v3s16 ( 0 , 0 , 1 ) , // back
v3s16 ( 0 , 1 , 0 ) , // top
v3s16 ( 1 , 0 , 0 ) , // right
v3s16 ( 0 , 0 , - 1 ) , // front
v3s16 ( 0 , - 1 , 0 ) , // bottom
v3s16 ( - 1 , 0 , 0 ) , // left
} ;
for ( u16 i = 0 ; i < 7 ; i + + )
{
try
{
v3s16 p2 = p + dirs [ i ] ;
2011-06-25 03:25:14 +02:00
2011-01-17 20:15:31 +01:00
MapNode n2 = getNode ( p2 ) ;
2012-07-26 21:06:45 +02:00
if ( ndef - > get ( n2 ) . isLiquid ( ) | | n2 . getContent ( ) = = CONTENT_AIR )
2011-01-17 20:15:31 +01:00
{
m_transforming_liquid . push_back ( p2 ) ;
}
2011-06-25 03:25:14 +02:00
2011-01-17 20:15:31 +01:00
} catch ( InvalidPositionException & e )
{
}
}
2010-11-27 00:02:21 +01:00
}
/*
*/
void Map : : removeNodeAndUpdate ( v3s16 p ,
core : : map < v3s16 , MapBlock * > & modified_blocks )
{
2012-07-26 21:06:45 +02:00
INodeDefManager * ndef = m_gamedef - > ndef ( ) ;
2011-11-14 20:41:30 +01:00
2010-11-27 00:02:21 +01:00
/*PrintInfo(m_dout);
m_dout < < DTIME < < " Map::removeNodeAndUpdate(): p=( "
< < p . X < < " , " < < p . Y < < " , " < < p . Z < < " ) " < < std : : endl ; */
2011-06-25 03:25:14 +02:00
2010-11-27 00:02:21 +01:00
bool node_under_sunlight = true ;
2011-06-25 03:25:14 +02:00
2010-11-27 00:02:21 +01:00
v3s16 toppos = p + v3s16 ( 0 , 1 , 0 ) ;
2010-11-30 14:35:03 +01:00
// Node will be replaced with this
2011-07-23 15:55:26 +02:00
content_t replace_material = CONTENT_AIR ;
2011-06-25 03:25:14 +02:00
2012-07-26 21:06:45 +02:00
/*
Collect old node for rollback
*/
RollbackNode rollback_oldnode ( this , p , m_gamedef ) ;
2010-11-27 00:02:21 +01:00
/*
If there is a node at top and it doesn ' t have sunlight ,
there will be no sunlight going down .
*/
try {
MapNode topnode = getNode ( toppos ) ;
2012-07-26 21:06:45 +02:00
if ( topnode . getLight ( LIGHTBANK_DAY , ndef ) ! = LIGHT_SUN )
2010-11-27 00:02:21 +01:00
node_under_sunlight = false ;
}
catch ( InvalidPositionException & e )
{
}
core : : map < v3s16 , bool > light_sources ;
2010-12-18 16:46:00 +01:00
enum LightBank banks [ ] =
{
LIGHTBANK_DAY ,
LIGHTBANK_NIGHT
} ;
for ( s32 i = 0 ; i < 2 ; i + + )
{
enum LightBank bank = banks [ i ] ;
2011-06-25 03:25:14 +02:00
2010-12-18 16:46:00 +01:00
/*
Unlight neighbors ( in case the node is a light source )
*/
unLightNeighbors ( bank , p ,
2012-07-26 21:06:45 +02:00
getNode ( p ) . getLight ( bank , ndef ) ,
2010-12-18 16:46:00 +01:00
light_sources , modified_blocks ) ;
}
2010-11-27 00:02:21 +01:00
2011-04-04 02:45:08 +02:00
/*
Remove node metadata
*/
removeNodeMetadata ( p ) ;
2010-11-27 00:02:21 +01:00
/*
2010-12-18 16:46:00 +01:00
Remove the node .
This also clears the lighting .
2010-11-27 00:02:21 +01:00
*/
2010-12-18 16:46:00 +01:00
2010-11-27 00:02:21 +01:00
MapNode n ;
2011-07-23 15:55:26 +02:00
n . setContent ( replace_material ) ;
2010-11-27 00:02:21 +01:00
setNode ( p , n ) ;
2011-06-25 03:25:14 +02:00
2010-12-18 16:46:00 +01:00
for ( s32 i = 0 ; i < 2 ; i + + )
{
enum LightBank bank = banks [ i ] ;
2011-06-25 03:25:14 +02:00
2010-12-18 16:46:00 +01:00
/*
Recalculate lighting
*/
spreadLight ( bank , light_sources , modified_blocks ) ;
}
2010-11-27 00:02:21 +01:00
// Add the block of the removed node to modified_blocks
v3s16 blockpos = getNodeBlockPos ( p ) ;
MapBlock * block = getBlockNoCreate ( blockpos ) ;
assert ( block ! = NULL ) ;
modified_blocks . insert ( blockpos , block ) ;
/*
If the removed node was under sunlight , propagate the
sunlight down from it and then light all neighbors
of the propagated blocks .
*/
if ( node_under_sunlight )
{
s16 ybottom = propagateSunlight ( p , modified_blocks ) ;
/*m_dout<<DTIME<<"Node was under sunlight. "
" Propagating sunlight " ;
m_dout < < DTIME < < " -> ybottom= " < < ybottom < < std : : endl ; */
s16 y = p . Y ;
for ( ; y > = ybottom ; y - - )
{
v3s16 p2 ( p . X , y , p . Z ) ;
/*m_dout<<DTIME<<"lighting neighbors of node ("
< < p2 . X < < " , " < < p2 . Y < < " , " < < p2 . Z < < " ) "
< < std : : endl ; */
2010-12-18 16:46:00 +01:00
lightNeighbors ( LIGHTBANK_DAY , p2 , modified_blocks ) ;
2010-11-27 00:02:21 +01:00
}
}
else
{
// Set the lighting of this node to 0
2010-12-18 16:46:00 +01:00
// TODO: Is this needed? Lighting is cleared up there already.
2010-11-27 00:02:21 +01:00
try {
MapNode n = getNode ( p ) ;
2012-07-26 21:06:45 +02:00
n . setLight ( LIGHTBANK_DAY , 0 , ndef ) ;
2010-11-27 00:02:21 +01:00
setNode ( p , n ) ;
}
catch ( InvalidPositionException & e )
{
2011-02-23 01:49:57 +01:00
assert ( 0 ) ;
2010-11-27 00:02:21 +01:00
}
}
2010-12-18 16:46:00 +01:00
for ( s32 i = 0 ; i < 2 ; i + + )
{
enum LightBank bank = banks [ i ] ;
2011-06-25 03:25:14 +02:00
2010-12-18 16:46:00 +01:00
// Get the brightest neighbour node and propagate light from it
v3s16 n2p = getBrightestNeighbour ( bank , p ) ;
try {
MapNode n2 = getNode ( n2p ) ;
lightNeighbors ( bank , n2p , modified_blocks ) ;
}
catch ( InvalidPositionException & e )
{
}
2010-11-27 00:02:21 +01:00
}
2010-12-19 15:51:45 +01:00
/*
Update information about whether day and night light differ
*/
for ( core : : map < v3s16 , MapBlock * > : : Iterator
i = modified_blocks . getIterator ( ) ;
i . atEnd ( ) = = false ; i + + )
{
MapBlock * block = i . getNode ( ) - > getValue ( ) ;
2012-03-28 23:28:48 +02:00
block - > expireDayNightDiff ( ) ;
2010-12-19 15:51:45 +01:00
}
2011-01-17 20:15:31 +01:00
2012-07-26 21:06:45 +02:00
/*
Report for rollback
*/
if ( m_gamedef - > rollback ( ) )
{
RollbackNode rollback_newnode ( this , p , m_gamedef ) ;
RollbackAction action ;
action . setSetNode ( p , rollback_oldnode , rollback_newnode ) ;
m_gamedef - > rollback ( ) - > reportAction ( action ) ;
}
2011-01-17 20:15:31 +01:00
/*
2011-07-17 04:01:31 +02:00
Add neighboring liquid nodes and this node to transform queue .
( it ' s vital for the node itself to get updated last . )
2011-01-17 20:15:31 +01:00
*/
2011-07-17 04:01:31 +02:00
v3s16 dirs [ 7 ] = {
2011-01-17 20:15:31 +01:00
v3s16 ( 0 , 0 , 1 ) , // back
v3s16 ( 0 , 1 , 0 ) , // top
v3s16 ( 1 , 0 , 0 ) , // right
v3s16 ( 0 , 0 , - 1 ) , // front
v3s16 ( 0 , - 1 , 0 ) , // bottom
v3s16 ( - 1 , 0 , 0 ) , // left
2011-07-17 04:01:31 +02:00
v3s16 ( 0 , 0 , 0 ) , // self
2011-01-17 20:15:31 +01:00
} ;
2011-07-17 04:01:31 +02:00
for ( u16 i = 0 ; i < 7 ; i + + )
2011-01-17 20:15:31 +01:00
{
try
{
v3s16 p2 = p + dirs [ i ] ;
2011-06-25 03:25:14 +02:00
2011-01-17 20:15:31 +01:00
MapNode n2 = getNode ( p2 ) ;
2012-07-26 21:06:45 +02:00
if ( ndef - > get ( n2 ) . isLiquid ( ) | | n2 . getContent ( ) = = CONTENT_AIR )
2011-01-17 20:15:31 +01:00
{
m_transforming_liquid . push_back ( p2 ) ;
}
2011-06-25 03:25:14 +02:00
2011-01-17 20:15:31 +01:00
} catch ( InvalidPositionException & e )
{
}
}
2010-12-18 16:46:00 +01:00
}
2011-02-23 01:49:57 +01:00
bool Map : : addNodeWithEvent ( v3s16 p , MapNode n )
{
MapEditEvent event ;
event . type = MEET_ADDNODE ;
event . p = p ;
event . n = n ;
bool succeeded = true ;
try {
core : : map < v3s16 , MapBlock * > modified_blocks ;
2012-01-21 00:11:44 +01:00
addNodeAndUpdate ( p , n , modified_blocks ) ;
2011-02-23 01:49:57 +01:00
// Copy modified_blocks to event
for ( core : : map < v3s16 , MapBlock * > : : Iterator
i = modified_blocks . getIterator ( ) ;
i . atEnd ( ) = = false ; i + + )
{
event . modified_blocks . insert ( i . getNode ( ) - > getKey ( ) , false ) ;
}
}
catch ( InvalidPositionException & e ) {
succeeded = false ;
}
dispatchEvent ( & event ) ;
return succeeded ;
}
bool Map : : removeNodeWithEvent ( v3s16 p )
{
MapEditEvent event ;
event . type = MEET_REMOVENODE ;
event . p = p ;
bool succeeded = true ;
try {
core : : map < v3s16 , MapBlock * > modified_blocks ;
removeNodeAndUpdate ( p , modified_blocks ) ;
// Copy modified_blocks to event
for ( core : : map < v3s16 , MapBlock * > : : Iterator
i = modified_blocks . getIterator ( ) ;
i . atEnd ( ) = = false ; i + + )
{
event . modified_blocks . insert ( i . getNode ( ) - > getKey ( ) , false ) ;
}
}
catch ( InvalidPositionException & e ) {
succeeded = false ;
}
dispatchEvent ( & event ) ;
return succeeded ;
}
2012-03-28 23:28:48 +02:00
bool Map : : getDayNightDiff ( v3s16 blockpos )
2010-12-19 15:51:45 +01:00
{
try {
v3s16 p = blockpos + v3s16 ( 0 , 0 , 0 ) ;
MapBlock * b = getBlockNoCreate ( p ) ;
2012-03-28 23:28:48 +02:00
if ( b - > getDayNightDiff ( ) )
2010-12-19 15:51:45 +01:00
return true ;
}
catch ( InvalidPositionException & e ) { }
2010-12-21 17:08:24 +01:00
// Leading edges
2010-12-19 15:51:45 +01:00
try {
2010-12-20 13:59:21 +01:00
v3s16 p = blockpos + v3s16 ( - 1 , 0 , 0 ) ;
2010-12-19 15:51:45 +01:00
MapBlock * b = getBlockNoCreate ( p ) ;
2012-03-28 23:28:48 +02:00
if ( b - > getDayNightDiff ( ) )
2010-12-19 15:51:45 +01:00
return true ;
}
catch ( InvalidPositionException & e ) { }
try {
2010-12-20 13:59:21 +01:00
v3s16 p = blockpos + v3s16 ( 0 , - 1 , 0 ) ;
2010-12-19 15:51:45 +01:00
MapBlock * b = getBlockNoCreate ( p ) ;
2012-03-28 23:28:48 +02:00
if ( b - > getDayNightDiff ( ) )
2010-12-19 15:51:45 +01:00
return true ;
}
catch ( InvalidPositionException & e ) { }
try {
2010-12-20 13:59:21 +01:00
v3s16 p = blockpos + v3s16 ( 0 , 0 , - 1 ) ;
2010-12-19 15:51:45 +01:00
MapBlock * b = getBlockNoCreate ( p ) ;
2012-03-28 23:28:48 +02:00
if ( b - > getDayNightDiff ( ) )
2010-12-19 15:51:45 +01:00
return true ;
2010-11-27 00:02:21 +01:00
}
catch ( InvalidPositionException & e ) { }
2010-12-21 17:08:24 +01:00
// Trailing edges
try {
v3s16 p = blockpos + v3s16 ( 1 , 0 , 0 ) ;
MapBlock * b = getBlockNoCreate ( p ) ;
2012-03-28 23:28:48 +02:00
if ( b - > getDayNightDiff ( ) )
2010-12-21 17:08:24 +01:00
return true ;
}
catch ( InvalidPositionException & e ) { }
try {
v3s16 p = blockpos + v3s16 ( 0 , 1 , 0 ) ;
MapBlock * b = getBlockNoCreate ( p ) ;
2012-03-28 23:28:48 +02:00
if ( b - > getDayNightDiff ( ) )
2010-12-21 17:08:24 +01:00
return true ;
}
catch ( InvalidPositionException & e ) { }
try {
v3s16 p = blockpos + v3s16 ( 0 , 0 , 1 ) ;
MapBlock * b = getBlockNoCreate ( p ) ;
2012-03-28 23:28:48 +02:00
if ( b - > getDayNightDiff ( ) )
2010-12-21 17:08:24 +01:00
return true ;
}
catch ( InvalidPositionException & e ) { }
2010-12-19 15:51:45 +01:00
return false ;
2010-11-27 00:02:21 +01:00
}
/*
Updates usage timers
*/
2011-06-26 23:27:17 +02:00
void Map : : timerUpdate ( float dtime , float unload_timeout ,
core : : list < v3s16 > * unloaded_blocks )
2010-11-27 00:02:21 +01:00
{
2011-06-26 23:27:17 +02:00
bool save_before_unloading = ( mapType ( ) = = MAPTYPE_SERVER ) ;
2012-12-22 06:34:35 +01:00
2011-11-21 12:35:32 +01:00
// Profile modified reasons
Profiler modprofiler ;
2012-12-22 06:34:35 +01:00
2011-06-26 23:27:17 +02:00
core : : list < v2s16 > sector_deletion_queue ;
u32 deleted_blocks_count = 0 ;
u32 saved_blocks_count = 0 ;
2011-11-25 17:08:47 +01:00
u32 block_count_all = 0 ;
2010-11-27 00:02:21 +01:00
core : : map < v2s16 , MapSector * > : : Iterator si ;
2011-09-06 18:16:36 +02:00
beginSave ( ) ;
2010-11-27 00:02:21 +01:00
si = m_sectors . getIterator ( ) ;
for ( ; si . atEnd ( ) = = false ; si + + )
{
MapSector * sector = si . getNode ( ) - > getValue ( ) ;
2011-06-25 03:25:14 +02:00
2011-06-26 23:27:17 +02:00
bool all_blocks_deleted = true ;
2011-06-25 03:25:14 +02:00
core : : list < MapBlock * > blocks ;
sector - > getBlocks ( blocks ) ;
2012-12-22 06:34:35 +01:00
2011-06-25 03:25:14 +02:00
for ( core : : list < MapBlock * > : : Iterator i = blocks . begin ( ) ;
i ! = blocks . end ( ) ; i + + )
{
2011-06-26 23:27:17 +02:00
MapBlock * block = ( * i ) ;
2012-12-22 06:34:35 +01:00
2011-06-26 23:27:17 +02:00
block - > incrementUsageTimer ( dtime ) ;
2012-09-04 08:48:26 +02:00
if ( block - > refGet ( ) = = 0 & & block - > getUsageTimer ( ) > unload_timeout )
2011-06-26 23:27:17 +02:00
{
v3s16 p = block - > getPos ( ) ;
// Save if modified
if ( block - > getModified ( ) ! = MOD_STATE_CLEAN
& & save_before_unloading )
{
2011-11-21 12:35:32 +01:00
modprofiler . add ( block - > getModifiedReason ( ) , 1 ) ;
2011-06-26 23:27:17 +02:00
saveBlock ( block ) ;
saved_blocks_count + + ;
}
// Delete from memory
sector - > deleteBlock ( block ) ;
if ( unloaded_blocks )
unloaded_blocks - > push_back ( p ) ;
deleted_blocks_count + + ;
}
else
{
all_blocks_deleted = false ;
2011-11-25 17:08:47 +01:00
block_count_all + + ;
2011-06-26 23:27:17 +02:00
}
2011-06-25 03:25:14 +02:00
}
2011-06-26 23:27:17 +02:00
if ( all_blocks_deleted )
{
sector_deletion_queue . push_back ( si . getNode ( ) - > getKey ( ) ) ;
2011-06-25 03:25:14 +02:00
}
2010-11-27 00:02:21 +01:00
}
2011-09-06 18:16:36 +02:00
endSave ( ) ;
2012-12-22 06:34:35 +01:00
2011-06-26 23:27:17 +02:00
// Finally delete the empty sectors
deleteSectors ( sector_deletion_queue ) ;
2012-12-22 06:34:35 +01:00
2011-06-26 23:27:17 +02:00
if ( deleted_blocks_count ! = 0 )
{
2011-10-16 13:57:53 +02:00
PrintInfo ( infostream ) ; // ServerMap/ClientMap:
infostream < < " Unloaded " < < deleted_blocks_count
2011-06-26 23:27:17 +02:00
< < " blocks from memory " ;
if ( save_before_unloading )
2011-10-16 13:57:53 +02:00
infostream < < " , of which " < < saved_blocks_count < < " were written " ;
2011-11-25 17:08:47 +01:00
infostream < < " , " < < block_count_all < < " blocks in memory " ;
2011-10-16 13:57:53 +02:00
infostream < < " . " < < std : : endl ;
2011-11-21 12:44:10 +01:00
if ( saved_blocks_count ! = 0 ) {
PrintInfo ( infostream ) ; // ServerMap/ClientMap:
infostream < < " Blocks modified by: " < < std : : endl ;
modprofiler . print ( infostream ) ;
}
2010-11-27 00:02:21 +01:00
}
}
2011-06-26 20:53:11 +02:00
void Map : : deleteSectors ( core : : list < v2s16 > & list )
2010-11-27 00:02:21 +01:00
{
core : : list < v2s16 > : : Iterator j ;
for ( j = list . begin ( ) ; j ! = list . end ( ) ; j + + )
{
MapSector * sector = m_sectors [ * j ] ;
2011-06-26 20:53:11 +02:00
// If sector is in sector cache, remove it from there
if ( m_sector_cache = = sector )
m_sector_cache = NULL ;
// Remove from map and delete
m_sectors . remove ( * j ) ;
delete sector ;
2010-11-27 00:02:21 +01:00
}
}
2011-06-26 23:27:17 +02:00
#if 0
2011-06-26 20:53:11 +02:00
void Map : : unloadUnusedData ( float timeout ,
2010-11-27 00:02:21 +01:00
core : : list < v3s16 > * deleted_blocks )
{
core : : list < v2s16 > sector_deletion_queue ;
2011-06-26 20:53:11 +02:00
u32 deleted_blocks_count = 0 ;
u32 saved_blocks_count = 0 ;
2011-06-25 03:25:14 +02:00
core : : map < v2s16 , MapSector * > : : Iterator si = m_sectors . getIterator ( ) ;
for ( ; si . atEnd ( ) = = false ; si + + )
{
MapSector * sector = si . getNode ( ) - > getValue ( ) ;
bool all_blocks_deleted = true ;
core : : list < MapBlock * > blocks ;
sector - > getBlocks ( blocks ) ;
for ( core : : list < MapBlock * > : : Iterator i = blocks . begin ( ) ;
i ! = blocks . end ( ) ; i + + )
{
MapBlock * block = ( * i ) ;
2012-12-22 06:34:35 +01:00
2011-06-25 03:25:14 +02:00
if ( block - > getUsageTimer ( ) > timeout )
{
// Save if modified
if ( block - > getModified ( ) ! = MOD_STATE_CLEAN )
2011-06-26 20:53:11 +02:00
{
2011-06-25 03:25:14 +02:00
saveBlock ( block ) ;
2011-06-26 20:53:11 +02:00
saved_blocks_count + + ;
}
2011-06-25 23:03:58 +02:00
// Delete from memory
sector - > deleteBlock ( block ) ;
2011-06-26 20:53:11 +02:00
deleted_blocks_count + + ;
2011-06-25 03:25:14 +02:00
}
else
{
all_blocks_deleted = false ;
}
}
if ( all_blocks_deleted )
{
sector_deletion_queue . push_back ( si . getNode ( ) - > getKey ( ) ) ;
}
}
2011-06-26 20:53:11 +02:00
deleteSectors ( sector_deletion_queue ) ;
2011-06-25 03:25:14 +02:00
2011-10-16 13:57:53 +02:00
infostream < < " Map: Unloaded " < < deleted_blocks_count < < " blocks from memory "
2011-06-26 20:53:11 +02:00
< < " , of which " < < saved_blocks_count < < " were wr. "
< < std : : endl ;
2011-06-25 03:25:14 +02:00
2011-06-26 20:53:11 +02:00
//return sector_deletion_queue.getSize();
//return deleted_blocks_count;
2010-11-27 00:02:21 +01:00
}
2011-06-26 23:27:17 +02:00
# endif
2010-11-27 00:02:21 +01:00
void Map : : PrintInfo ( std : : ostream & out )
{
out < < " Map: " ;
}
2011-01-17 20:15:31 +01:00
# define WATER_DROP_BOOST 4
2011-07-16 16:01:37 +02:00
enum NeighborType {
NEIGHBOR_UPPER ,
NEIGHBOR_SAME_LEVEL ,
NEIGHBOR_LOWER
} ;
struct NodeNeighbor {
MapNode n ;
NeighborType t ;
v3s16 p ;
2013-02-24 15:39:07 +01:00
bool l ; //can liquid
bool i ; //infinity
2011-07-16 16:01:37 +02:00
} ;
2013-02-24 15:39:07 +01:00
void Map : : transforming_liquid_add ( v3s16 p ) {
m_transforming_liquid . push_back ( p ) ;
}
s32 Map : : transforming_liquid_size ( ) {
return m_transforming_liquid . size ( ) ;
}
const v3s16 g_7dirs [ 7 ] =
{
// +right, +top, +back
v3s16 ( 0 , - 1 , 0 ) , // bottom
v3s16 ( 0 , 0 , 0 ) , // self
v3s16 ( 0 , 0 , 1 ) , // back
v3s16 ( 0 , 0 , - 1 ) , // front
v3s16 ( 1 , 0 , 0 ) , // right
v3s16 ( - 1 , 0 , 0 ) , // left
v3s16 ( 0 , 1 , 0 ) // top
} ;
# define D_BOTTOM 0
# define D_TOP 6
# define D_SELF 1
void Map : : transformLiquidsFinite ( core : : map < v3s16 , MapBlock * > & modified_blocks )
{
INodeDefManager * nodemgr = m_gamedef - > ndef ( ) ;
DSTACK ( __FUNCTION_NAME ) ;
//TimeTaker timer("transformLiquids()");
u32 loopcount = 0 ;
u32 initial_size = m_transforming_liquid . size ( ) ;
u8 relax = g_settings - > getS16 ( " liquid_relax " ) ;
bool fast_flood = g_settings - > getS16 ( " liquid_fast_flood " ) ;
int water_level = g_settings - > getS16 ( " water_level " ) ;
/*if(initial_size != 0)
infostream < < " transformLiquids(): initial_size= " < < initial_size < < std : : endl ; */
// list of nodes that due to viscosity have not reached their max level height
UniqueQueue < v3s16 > must_reflow , must_reflow_second ;
// List of MapBlocks that will require a lighting update (due to lava)
core : : map < v3s16 , MapBlock * > lighting_modified_blocks ;
while ( m_transforming_liquid . size ( ) > 0 )
{
// This should be done here so that it is done when continue is used
if ( loopcount > = initial_size | | loopcount > = 1000 )
break ;
loopcount + + ;
/*
Get a queued transforming liquid node
*/
v3s16 p0 = m_transforming_liquid . pop_front ( ) ;
u16 total_level = 0 ;
NodeNeighbor neighbors [ 7 ] ; // surrounding flowing liquid nodes
s8 liquid_levels [ 7 ] = { - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 } ; // current level of every block
s8 liquid_levels_want [ 7 ] = { - 1 , - 1 , - 1 , - 1 , - 1 , - 1 , - 1 } ; // target levels
s8 can_liquid_same_level = 0 ;
content_t liquid_kind = CONTENT_IGNORE ;
content_t liquid_kind_flowing = CONTENT_IGNORE ;
/*
Collect information about the environment
*/
const v3s16 * dirs = g_7dirs ;
for ( u16 i = 0 ; i < 7 ; i + + ) {
NeighborType nt = NEIGHBOR_SAME_LEVEL ;
switch ( i ) {
case D_TOP :
nt = NEIGHBOR_UPPER ;
break ;
case D_BOTTOM :
nt = NEIGHBOR_LOWER ;
break ;
}
v3s16 npos = p0 + dirs [ i ] ;
neighbors [ i ] . n = getNodeNoEx ( npos ) ;
neighbors [ i ] . t = nt ;
neighbors [ i ] . p = npos ;
neighbors [ i ] . l = 0 ;
neighbors [ i ] . i = 0 ;
NodeNeighbor & nb = neighbors [ i ] ;
switch ( nodemgr - > get ( nb . n . getContent ( ) ) . liquid_type ) {
case LIQUID_NONE :
if ( nb . n . getContent ( ) = = CONTENT_AIR ) {
liquid_levels [ i ] = 0 ;
nb . l = 1 ;
}
break ;
case LIQUID_SOURCE :
// if this node is not (yet) of a liquid type, choose the first liquid type we encounter
if ( liquid_kind_flowing = = CONTENT_IGNORE )
liquid_kind_flowing = nodemgr - > getId ( nodemgr - > get ( nb . n ) . liquid_alternative_flowing ) ;
if ( liquid_kind = = CONTENT_IGNORE )
liquid_kind = nb . n . getContent ( ) ;
if ( nb . n . getContent ( ) = = liquid_kind ) {
liquid_levels [ i ] = LIQUID_LEVEL_SOURCE ;
nb . l = 1 ;
nb . i = ( nb . n . param2 & LIQUID_INFINITY_MASK ) ;
}
break ;
case LIQUID_FLOWING :
// if this node is not (yet) of a liquid type, choose the first liquid type we encounter
if ( liquid_kind_flowing = = CONTENT_IGNORE )
liquid_kind_flowing = nb . n . getContent ( ) ;
if ( liquid_kind = = CONTENT_IGNORE )
liquid_kind = nodemgr - > getId ( nodemgr - > get ( nb . n ) . liquid_alternative_source ) ;
if ( nb . n . getContent ( ) = = liquid_kind_flowing ) {
liquid_levels [ i ] = ( nb . n . param2 & LIQUID_LEVEL_MASK ) ;
nb . l = 1 ;
}
break ;
}
if ( nb . l & & nb . t = = NEIGHBOR_SAME_LEVEL ) + + can_liquid_same_level ;
if ( liquid_levels [ i ] > 0 ) total_level + = liquid_levels [ i ] ;
/*
infostream < < " get node i= " < < ( int ) i < < " " < < PP ( npos ) < < " c= " < < nb . n . getContent ( ) < < " p0= " < < ( int ) nb . n . param0 < < " p1= " < < ( int ) nb . n . param1 < < " p2= " < < ( int ) nb . n . param2 < < " lt= " < < nodemgr - > get ( nb . n . getContent ( ) ) . liquid_type
//<< " lk=" << liquid_kind << " lkf=" << liquid_kind_flowing
< < " l= " < < nb . l < < " inf= " < < nb . i < < " nlevel= " < < ( int ) liquid_levels [ i ] < < " tlevel= " < < ( int ) total_level < < " cansame= " < < ( int ) can_liquid_same_level < < std : : endl ;
*/
}
if ( liquid_kind = = CONTENT_IGNORE | | ! neighbors [ D_SELF ] . l | | total_level < = 0 )
continue ;
// fill bottom block
if ( neighbors [ D_BOTTOM ] . l ) {
liquid_levels_want [ D_BOTTOM ] = total_level > LIQUID_LEVEL_SOURCE ? LIQUID_LEVEL_SOURCE : total_level ;
total_level - = liquid_levels_want [ D_BOTTOM ] ;
}
if ( relax & & p0 . Y < = water_level & & liquid_levels [ D_TOP ] = = 0 & & total_level > = LIQUID_LEVEL_SOURCE * can_liquid_same_level - can_liquid_same_level + 2 & & can_liquid_same_level > = relax + 1 ) { //relax up
total_level = LIQUID_LEVEL_SOURCE * can_liquid_same_level ;
}
// calculate self level 5 blocks
u8 want_level =
total_level > = LIQUID_LEVEL_SOURCE * can_liquid_same_level
? LIQUID_LEVEL_SOURCE
: total_level / can_liquid_same_level ;
total_level - = want_level * can_liquid_same_level ;
if ( relax & & p0 . Y > water_level & & liquid_levels [ D_TOP ] = = 0 & & liquid_levels [ D_BOTTOM ] = = LIQUID_LEVEL_SOURCE & & want_level = = 0 & & total_level < = can_liquid_same_level - 2 & & can_liquid_same_level > = relax + 1 ) { //relax down
total_level = 0 ;
}
for ( u16 ii = D_SELF ; ii < D_TOP ; + + ii ) { // fill only same level
if ( ! neighbors [ ii ] . l )
continue ;
liquid_levels_want [ ii ] = want_level ;
if ( liquid_levels_want [ ii ] < LIQUID_LEVEL_SOURCE & & total_level > 0
& & liquid_levels [ ii ] > liquid_levels_want [ ii ]
) {
+ + liquid_levels_want [ ii ] ;
- - total_level ;
}
}
for ( u16 ii = 0 ; ii < 7 ; + + ii ) {
if ( total_level < 1 ) break ;
if ( liquid_levels_want [ ii ] > = 0 & & liquid_levels_want [ ii ] < LIQUID_LEVEL_SOURCE ) {
+ + liquid_levels_want [ ii ] ;
- - total_level ;
}
}
// fill top block if can
if ( neighbors [ D_TOP ] . l ) {
liquid_levels_want [ D_TOP ] = total_level > LIQUID_LEVEL_SOURCE ? LIQUID_LEVEL_SOURCE : total_level ;
total_level - = liquid_levels_want [ D_TOP ] ;
}
for ( u16 ii = 0 ; ii < 7 ; ii + + ) // infinity and cave flood optimization
if ( liquid_levels_want [ ii ] > = 0 & &
( neighbors [ ii ] . i | |
( fast_flood & & p0 . Y < water_level & &
( initial_size > = 1000
& & ii ! = D_TOP
& & want_level > = LIQUID_LEVEL_SOURCE / 4
& & can_liquid_same_level > = 5
& & liquid_levels [ D_TOP ] > = LIQUID_LEVEL_SOURCE ) ) ) )
liquid_levels_want [ ii ] = LIQUID_LEVEL_SOURCE ;
//if (total_level > 0 /*|| flowed != volume*/) infostream <<" AFTER level=" << (int)total_level /*<< " flowed="<<flowed<< " volume=" <<volume*/<< " wantsame="<<(int)want_level<< " top="<< (int)liquid_levels_want[D_TOP]<< " topwas="<< (int)liquid_levels[D_TOP]<< " bot="<< (int)liquid_levels_want[D_BOTTOM]<<std::endl;
u8 changed = 0 ;
for ( u16 i = 0 ; i < 7 ; i + + ) {
if ( liquid_levels_want [ i ] < 0 | | ! neighbors [ i ] . l )
continue ;
MapNode & n0 = neighbors [ i ] . n ;
p0 = neighbors [ i ] . p ;
/*
decide on the type ( and possibly level ) of the current node
*/
content_t new_node_content ;
s8 new_node_level = - 1 ;
u8 viscosity = nodemgr - > get ( liquid_kind ) . liquid_viscosity ;
if ( viscosity > 1 & & liquid_levels_want [ i ] ! = liquid_levels [ i ] ) {
// amount to gain, limited by viscosity
// must be at least 1 in absolute value
s8 level_inc = liquid_levels_want [ i ] - liquid_levels [ i ] ;
if ( level_inc < - viscosity | | level_inc > viscosity )
new_node_level = liquid_levels [ i ] + level_inc / viscosity ;
else if ( level_inc < 0 )
new_node_level = liquid_levels [ i ] - 1 ;
else if ( level_inc > 0 )
new_node_level = liquid_levels [ i ] + 1 ;
} else
new_node_level = liquid_levels_want [ i ] ;
if ( new_node_level > = LIQUID_LEVEL_SOURCE )
new_node_content = liquid_kind ;
else if ( new_node_level > 0 )
new_node_content = liquid_kind_flowing ;
else
new_node_content = CONTENT_AIR ;
// last level must flow down on stairs
if ( liquid_levels_want [ i ] ! = liquid_levels [ i ] & & liquid_levels [ D_TOP ] < = 0 & & ! neighbors [ D_BOTTOM ] . l & & new_node_level > = 1 & & new_node_level < = 2 ) //maybe == 1 //
for ( u16 ii = D_SELF + 1 ; ii < D_TOP ; + + ii ) { // only same level
if ( ! neighbors [ ii ] . l )
continue ;
must_reflow_second . push_back ( p0 + dirs [ ii ] ) ;
}
/*
check if anything has changed . if not , just continue with the next node .
*/
if (
new_node_content = = n0 . getContent ( )
& & ( nodemgr - > get ( n0 . getContent ( ) ) . liquid_type ! = LIQUID_FLOWING | |
( ( n0 . param2 & LIQUID_LEVEL_MASK ) = = ( u8 ) new_node_level
// &&((n0.param2 & LIQUID_FLOW_DOWN_MASK) == LIQUID_FLOW_DOWN_MASK)== flowing_down
) )
& &
( nodemgr - > get ( n0 . getContent ( ) ) . liquid_type ! = LIQUID_SOURCE | |
( ( ( n0 . param2 & LIQUID_INFINITY_MASK ) = = LIQUID_INFINITY_MASK ) = = neighbors [ i ] . i
) )
) {
continue ;
}
+ + changed ;
/*
update the current node
*/
if ( nodemgr - > get ( new_node_content ) . liquid_type = = LIQUID_FLOWING ) {
// set level to last 3 bits, flowing down bit to 4th bit
n0 . param2 = ( new_node_level & LIQUID_LEVEL_MASK ) ;
} else if ( nodemgr - > get ( new_node_content ) . liquid_type = = LIQUID_SOURCE ) {
//n0.param2 = ~(LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK);
n0 . param2 = ( neighbors [ i ] . i ? LIQUID_INFINITY_MASK : 0x00 ) ;
}
//infostream << "set node i=" <<(int)i<<" "<< PP(p0)<< " nc="<<new_node_content<< " p2="<<(int)n0.param2<< " nl="<<(int)new_node_level<<std::endl;
n0 . setContent ( new_node_content ) ;
// Find out whether there is a suspect for this action
std : : string suspect ;
if ( m_gamedef - > rollback ( ) ) {
suspect = m_gamedef - > rollback ( ) - > getSuspect ( p0 , 83 , 1 ) ;
}
if ( ! suspect . empty ( ) ) {
// Blame suspect
RollbackScopeActor rollback_scope ( m_gamedef - > rollback ( ) , suspect , true ) ;
// Get old node for rollback
RollbackNode rollback_oldnode ( this , p0 , m_gamedef ) ;
// Set node
setNode ( p0 , n0 ) ;
// Report
RollbackNode rollback_newnode ( this , p0 , m_gamedef ) ;
RollbackAction action ;
action . setSetNode ( p0 , rollback_oldnode , rollback_newnode ) ;
m_gamedef - > rollback ( ) - > reportAction ( action ) ;
} else {
// Set node
setNode ( p0 , n0 ) ;
}
v3s16 blockpos = getNodeBlockPos ( p0 ) ;
MapBlock * block = getBlockNoCreateNoEx ( blockpos ) ;
if ( block ! = NULL ) {
modified_blocks . insert ( blockpos , block ) ;
// If node emits light, MapBlock requires lighting update
if ( nodemgr - > get ( n0 ) . light_source ! = 0 )
lighting_modified_blocks [ block - > getPos ( ) ] = block ;
}
must_reflow . push_back ( neighbors [ i ] . p ) ;
}
/* //for better relax
if ( changed ) for ( u16 ii = D_SELF + 1 ; ii < D_TOP ; + + ii ) { // only same level
if ( ! neighbors [ ii ] . l ) continue ;
must_reflow . push_back ( p0 + dirs [ ii ] ) ;
} */
}
//if (loopcount) infostream<<"Map::transformLiquids(): loopcount="<<loopcount<<" reflow="<<must_reflow.size()<<" queue="<< m_transforming_liquid.size()<<std::endl;
while ( must_reflow . size ( ) > 0 )
m_transforming_liquid . push_back ( must_reflow . pop_front ( ) ) ;
while ( must_reflow_second . size ( ) > 0 )
m_transforming_liquid . push_back ( must_reflow_second . pop_front ( ) ) ;
updateLighting ( lighting_modified_blocks , modified_blocks ) ;
}
2011-01-17 20:15:31 +01:00
void Map : : transformLiquids ( core : : map < v3s16 , MapBlock * > & modified_blocks )
{
2013-02-24 15:39:07 +01:00
if ( g_settings - > getBool ( " liquid_finite " ) ) return Map : : transformLiquidsFinite ( modified_blocks ) ;
2011-11-14 20:41:30 +01:00
INodeDefManager * nodemgr = m_gamedef - > ndef ( ) ;
2011-01-17 20:15:31 +01:00
DSTACK ( __FUNCTION_NAME ) ;
2011-01-17 20:18:08 +01:00
//TimeTaker timer("transformLiquids()");
2011-01-17 20:15:31 +01:00
u32 loopcount = 0 ;
u32 initial_size = m_transforming_liquid . size ( ) ;
2011-06-25 03:25:14 +02:00
2011-02-08 00:12:55 +01:00
/*if(initial_size != 0)
2011-10-16 13:57:53 +02:00
infostream < < " transformLiquids(): initial_size= " < < initial_size < < std : : endl ; */
2011-01-30 00:44:54 +01:00
2011-08-16 19:56:57 +02:00
// list of nodes that due to viscosity have not reached their max level height
UniqueQueue < v3s16 > must_reflow ;
2012-12-22 06:34:35 +01:00
2011-08-22 12:19:07 +02:00
// List of MapBlocks that will require a lighting update (due to lava)
core : : map < v3s16 , MapBlock * > lighting_modified_blocks ;
2011-08-16 19:56:57 +02:00
2011-01-17 20:15:31 +01:00
while ( m_transforming_liquid . size ( ) ! = 0 )
{
2011-08-15 17:49:44 +02:00
// This should be done here so that it is done when continue is used
2012-07-27 14:46:51 +02:00
if ( loopcount > = initial_size | | loopcount > = 10000 )
2011-08-15 17:49:44 +02:00
break ;
loopcount + + ;
2011-01-23 16:29:15 +01:00
/*
Get a queued transforming liquid node
*/
2011-01-17 20:15:31 +01:00
v3s16 p0 = m_transforming_liquid . pop_front ( ) ;
2011-06-26 23:27:17 +02:00
MapNode n0 = getNodeNoEx ( p0 ) ;
2011-08-16 06:00:40 +02:00
2011-01-17 20:15:31 +01:00
/*
2011-07-16 16:01:37 +02:00
Collect information about current node
*/
s8 liquid_level = - 1 ;
2013-01-12 20:20:21 +01:00
content_t liquid_kind = CONTENT_IGNORE ;
2011-11-14 20:41:30 +01:00
LiquidType liquid_type = nodemgr - > get ( n0 ) . liquid_type ;
2011-07-16 16:01:37 +02:00
switch ( liquid_type ) {
case LIQUID_SOURCE :
2011-08-16 08:31:33 +02:00
liquid_level = LIQUID_LEVEL_SOURCE ;
2011-11-25 16:00:50 +01:00
liquid_kind = nodemgr - > getId ( nodemgr - > get ( n0 ) . liquid_alternative_flowing ) ;
2011-07-16 16:01:37 +02:00
break ;
case LIQUID_FLOWING :
liquid_level = ( n0 . param2 & LIQUID_LEVEL_MASK ) ;
2011-07-30 18:53:54 +02:00
liquid_kind = n0 . getContent ( ) ;
2011-07-16 16:01:37 +02:00
break ;
case LIQUID_NONE :
// if this is an air node, it *could* be transformed into a liquid. otherwise,
// continue with the next node.
2011-07-30 18:53:54 +02:00
if ( n0 . getContent ( ) ! = CONTENT_AIR )
2011-07-16 16:01:37 +02:00
continue ;
liquid_kind = CONTENT_AIR ;
break ;
}
2011-08-16 06:00:40 +02:00
2011-07-16 16:01:37 +02:00
/*
Collect information about the environment
*/
2011-08-16 06:00:40 +02:00
const v3s16 * dirs = g_6dirs ;
2011-07-16 16:01:37 +02:00
NodeNeighbor sources [ 6 ] ; // surrounding sources
int num_sources = 0 ;
NodeNeighbor flows [ 6 ] ; // surrounding flowing liquid nodes
int num_flows = 0 ;
NodeNeighbor airs [ 6 ] ; // surrounding air
int num_airs = 0 ;
2011-07-17 13:55:59 +02:00
NodeNeighbor neutrals [ 6 ] ; // nodes that are solid or another kind of liquid
2011-07-16 16:01:37 +02:00
int num_neutrals = 0 ;
bool flowing_down = false ;
for ( u16 i = 0 ; i < 6 ; i + + ) {
NeighborType nt = NEIGHBOR_SAME_LEVEL ;
switch ( i ) {
2011-08-16 06:00:40 +02:00
case 1 :
2011-07-16 16:01:37 +02:00
nt = NEIGHBOR_UPPER ;
break ;
2011-08-16 06:00:40 +02:00
case 4 :
2011-07-16 16:01:37 +02:00
nt = NEIGHBOR_LOWER ;
break ;
}
v3s16 npos = p0 + dirs [ i ] ;
NodeNeighbor nb = { getNodeNoEx ( npos ) , nt , npos } ;
2011-11-14 20:41:30 +01:00
switch ( nodemgr - > get ( nb . n . getContent ( ) ) . liquid_type ) {
2011-07-16 16:01:37 +02:00
case LIQUID_NONE :
2011-07-30 18:53:54 +02:00
if ( nb . n . getContent ( ) = = CONTENT_AIR ) {
2011-07-16 16:01:37 +02:00
airs [ num_airs + + ] = nb ;
2011-08-16 07:24:01 +02:00
// if the current node is a water source the neighbor
// should be enqueded for transformation regardless of whether the
// current node changes or not.
if ( nb . t ! = NEIGHBOR_UPPER & & liquid_type ! = LIQUID_NONE )
m_transforming_liquid . push_back ( npos ) ;
2011-07-17 13:55:59 +02:00
// if the current node happens to be a flowing node, it will start to flow down here.
2011-08-16 07:24:01 +02:00
if ( nb . t = = NEIGHBOR_LOWER ) {
2011-07-16 16:01:37 +02:00
flowing_down = true ;
2011-08-16 07:24:01 +02:00
}
2011-07-16 16:01:37 +02:00
} else {
neutrals [ num_neutrals + + ] = nb ;
2011-01-17 20:15:31 +01:00
}
2011-07-16 16:01:37 +02:00
break ;
case LIQUID_SOURCE :
2012-12-22 06:34:35 +01:00
// if this node is not (yet) of a liquid type, choose the first liquid type we encounter
2011-07-16 16:01:37 +02:00
if ( liquid_kind = = CONTENT_AIR )
2011-11-25 16:00:50 +01:00
liquid_kind = nodemgr - > getId ( nodemgr - > get ( nb . n ) . liquid_alternative_flowing ) ;
if ( nodemgr - > getId ( nodemgr - > get ( nb . n ) . liquid_alternative_flowing ) ! = liquid_kind ) {
2011-07-16 16:01:37 +02:00
neutrals [ num_neutrals + + ] = nb ;
} else {
2011-11-29 17:21:00 +01:00
// Do not count bottom source, it will screw things up
if ( dirs [ i ] . Y ! = - 1 )
sources [ num_sources + + ] = nb ;
2011-01-17 20:15:31 +01:00
}
2011-07-16 16:01:37 +02:00
break ;
case LIQUID_FLOWING :
// if this node is not (yet) of a liquid type, choose the first liquid type we encounter
2011-07-17 13:15:33 +02:00
if ( liquid_kind = = CONTENT_AIR )
2011-11-25 16:00:50 +01:00
liquid_kind = nodemgr - > getId ( nodemgr - > get ( nb . n ) . liquid_alternative_flowing ) ;
if ( nodemgr - > getId ( nodemgr - > get ( nb . n ) . liquid_alternative_flowing ) ! = liquid_kind ) {
2011-07-16 16:01:37 +02:00
neutrals [ num_neutrals + + ] = nb ;
} else {
2011-07-17 13:55:59 +02:00
flows [ num_flows + + ] = nb ;
2011-07-16 16:01:37 +02:00
if ( nb . t = = NEIGHBOR_LOWER )
flowing_down = true ;
2011-01-17 20:15:31 +01:00
}
2011-07-16 16:01:37 +02:00
break ;
2011-01-17 20:15:31 +01:00
}
}
2011-08-16 06:00:40 +02:00
2011-01-17 20:15:31 +01:00
/*
2011-07-16 16:01:37 +02:00
decide on the type ( and possibly level ) of the current node
*/
2011-08-02 20:51:14 +02:00
content_t new_node_content ;
2011-07-16 16:01:37 +02:00
s8 new_node_level = - 1 ;
2011-08-16 19:56:57 +02:00
s8 max_node_level = - 1 ;
2012-09-07 18:48:12 +02:00
if ( ( num_sources > = 2 & & nodemgr - > get ( liquid_kind ) . liquid_renewable ) | | liquid_type = = LIQUID_SOURCE ) {
2011-07-16 16:01:37 +02:00
// liquid_kind will be set to either the flowing alternative of the node (if it's a liquid)
// or the flowing alternative of the first of the surrounding sources (if it's air), so
// it's perfectly safe to use liquid_kind here to determine the new node content.
2011-11-25 16:00:50 +01:00
new_node_content = nodemgr - > getId ( nodemgr - > get ( liquid_kind ) . liquid_alternative_source ) ;
2012-09-07 18:48:12 +02:00
} else if ( num_sources > = 1 & & sources [ 0 ] . t ! = NEIGHBOR_LOWER ) {
2011-07-16 16:01:37 +02:00
// liquid_kind is set properly, see above
new_node_content = liquid_kind ;
2011-08-16 19:56:57 +02:00
max_node_level = new_node_level = LIQUID_LEVEL_MAX ;
2011-07-16 16:01:37 +02:00
} else {
// no surrounding sources, so get the maximum level that can flow into this node
for ( u16 i = 0 ; i < num_flows ; i + + ) {
u8 nb_liquid_level = ( flows [ i ] . n . param2 & LIQUID_LEVEL_MASK ) ;
switch ( flows [ i ] . t ) {
case NEIGHBOR_UPPER :
2011-08-16 19:56:57 +02:00
if ( nb_liquid_level + WATER_DROP_BOOST > max_node_level ) {
max_node_level = LIQUID_LEVEL_MAX ;
2011-08-16 08:31:33 +02:00
if ( nb_liquid_level + WATER_DROP_BOOST < LIQUID_LEVEL_MAX )
2011-08-16 19:56:57 +02:00
max_node_level = nb_liquid_level + WATER_DROP_BOOST ;
2011-08-20 07:54:01 +02:00
} else if ( nb_liquid_level > max_node_level )
max_node_level = nb_liquid_level ;
2011-07-16 16:01:37 +02:00
break ;
case NEIGHBOR_LOWER :
break ;
case NEIGHBOR_SAME_LEVEL :
if ( ( flows [ i ] . n . param2 & LIQUID_FLOW_DOWN_MASK ) ! = LIQUID_FLOW_DOWN_MASK & &
2011-08-16 19:56:57 +02:00
nb_liquid_level > 0 & & nb_liquid_level - 1 > max_node_level ) {
max_node_level = nb_liquid_level - 1 ;
2011-07-16 16:01:37 +02:00
}
break ;
}
2011-01-17 20:15:31 +01:00
}
2011-08-16 06:00:40 +02:00
2011-11-14 20:41:30 +01:00
u8 viscosity = nodemgr - > get ( liquid_kind ) . liquid_viscosity ;
2011-08-16 20:38:44 +02:00
if ( viscosity > 1 & & max_node_level ! = liquid_level ) {
2011-08-16 19:56:57 +02:00
// amount to gain, limited by viscosity
// must be at least 1 in absolute value
s8 level_inc = max_node_level - liquid_level ;
if ( level_inc < - viscosity | | level_inc > viscosity )
new_node_level = liquid_level + level_inc / viscosity ;
else if ( level_inc < 0 )
new_node_level = liquid_level - 1 ;
else if ( level_inc > 0 )
new_node_level = liquid_level + 1 ;
if ( new_node_level ! = max_node_level )
must_reflow . push_back ( p0 ) ;
} else
new_node_level = max_node_level ;
2011-07-16 16:01:37 +02:00
if ( new_node_level > = 0 )
new_node_content = liquid_kind ;
2011-01-17 20:15:31 +01:00
else
2011-07-16 16:01:37 +02:00
new_node_content = CONTENT_AIR ;
2011-08-16 19:56:57 +02:00
2011-07-16 16:01:37 +02:00
}
2011-08-16 06:00:40 +02:00
2011-07-16 16:01:37 +02:00
/*
check if anything has changed . if not , just continue with the next node .
*/
2011-11-14 20:41:30 +01:00
if ( new_node_content = = n0 . getContent ( ) & & ( nodemgr - > get ( n0 . getContent ( ) ) . liquid_type ! = LIQUID_FLOWING | |
2011-07-17 04:01:31 +02:00
( ( n0 . param2 & LIQUID_LEVEL_MASK ) = = ( u8 ) new_node_level & &
2011-07-16 16:01:37 +02:00
( ( n0 . param2 & LIQUID_FLOW_DOWN_MASK ) = = LIQUID_FLOW_DOWN_MASK )
2011-07-17 04:01:31 +02:00
= = flowing_down ) ) )
2011-07-16 16:01:37 +02:00
continue ;
2011-08-16 06:00:40 +02:00
2011-07-16 16:01:37 +02:00
/*
update the current node
*/
2011-11-13 09:57:55 +01:00
//bool flow_down_enabled = (flowing_down && ((n0.param2 & LIQUID_FLOW_DOWN_MASK) != LIQUID_FLOW_DOWN_MASK));
2011-11-14 20:41:30 +01:00
if ( nodemgr - > get ( new_node_content ) . liquid_type = = LIQUID_FLOWING ) {
2011-07-16 16:01:37 +02:00
// set level to last 3 bits, flowing down bit to 4th bit
2011-08-16 16:46:55 +02:00
n0 . param2 = ( flowing_down ? LIQUID_FLOW_DOWN_MASK : 0x00 ) | ( new_node_level & LIQUID_LEVEL_MASK ) ;
2011-07-16 16:01:37 +02:00
} else {
2011-08-02 20:51:14 +02:00
// set the liquid level and flow bit to 0
2011-08-16 16:46:55 +02:00
n0 . param2 = ~ ( LIQUID_LEVEL_MASK | LIQUID_FLOW_DOWN_MASK ) ;
2011-07-16 16:01:37 +02:00
}
2011-08-16 16:46:55 +02:00
n0 . setContent ( new_node_content ) ;
2012-12-22 06:34:35 +01:00
2012-07-27 12:24:28 +02:00
// Find out whether there is a suspect for this action
std : : string suspect ;
if ( m_gamedef - > rollback ( ) ) {
2012-07-27 14:46:51 +02:00
suspect = m_gamedef - > rollback ( ) - > getSuspect ( p0 , 83 , 1 ) ;
2012-07-27 12:24:28 +02:00
}
if ( ! suspect . empty ( ) ) {
// Blame suspect
RollbackScopeActor rollback_scope ( m_gamedef - > rollback ( ) , suspect , true ) ;
// Get old node for rollback
RollbackNode rollback_oldnode ( this , p0 , m_gamedef ) ;
// Set node
setNode ( p0 , n0 ) ;
// Report
2012-07-26 21:06:45 +02:00
RollbackNode rollback_newnode ( this , p0 , m_gamedef ) ;
RollbackAction action ;
action . setSetNode ( p0 , rollback_oldnode , rollback_newnode ) ;
m_gamedef - > rollback ( ) - > reportAction ( action ) ;
2012-07-27 12:24:28 +02:00
} else {
// Set node
setNode ( p0 , n0 ) ;
}
2012-07-26 21:06:45 +02:00
2011-07-16 16:01:37 +02:00
v3s16 blockpos = getNodeBlockPos ( p0 ) ;
MapBlock * block = getBlockNoCreateNoEx ( blockpos ) ;
2011-08-22 12:19:07 +02:00
if ( block ! = NULL ) {
2011-07-16 16:01:37 +02:00
modified_blocks . insert ( blockpos , block ) ;
2011-08-22 12:19:07 +02:00
// If node emits light, MapBlock requires lighting update
2011-11-14 20:41:30 +01:00
if ( nodemgr - > get ( n0 ) . light_source ! = 0 )
2011-08-22 12:19:07 +02:00
lighting_modified_blocks [ block - > getPos ( ) ] = block ;
}
2011-08-16 06:00:40 +02:00
2011-07-17 04:01:31 +02:00
/*
enqueue neighbors for update if neccessary
*/
2011-11-14 20:41:30 +01:00
switch ( nodemgr - > get ( n0 . getContent ( ) ) . liquid_type ) {
2011-07-16 16:01:37 +02:00
case LIQUID_SOURCE :
2011-08-16 18:05:28 +02:00
case LIQUID_FLOWING :
2011-07-16 16:01:37 +02:00
// make sure source flows into all neighboring nodes
for ( u16 i = 0 ; i < num_flows ; i + + )
if ( flows [ i ] . t ! = NEIGHBOR_UPPER )
m_transforming_liquid . push_back ( flows [ i ] . p ) ;
for ( u16 i = 0 ; i < num_airs ; i + + )
if ( airs [ i ] . t ! = NEIGHBOR_UPPER )
m_transforming_liquid . push_back ( airs [ i ] . p ) ;
break ;
case LIQUID_NONE :
// this flow has turned to air; neighboring flows might need to do the same
for ( u16 i = 0 ; i < num_flows ; i + + )
m_transforming_liquid . push_back ( flows [ i ] . p ) ;
break ;
2011-01-17 20:15:31 +01:00
}
}
2011-10-16 13:57:53 +02:00
//infostream<<"Map::transformLiquids(): loopcount="<<loopcount<<std::endl;
2011-08-16 19:56:57 +02:00
while ( must_reflow . size ( ) > 0 )
m_transforming_liquid . push_back ( must_reflow . pop_front ( ) ) ;
2011-08-22 12:19:07 +02:00
updateLighting ( lighting_modified_blocks , modified_blocks ) ;
2011-01-17 20:15:31 +01:00
}
2011-04-04 02:45:08 +02:00
NodeMetadata * Map : : getNodeMetadata ( v3s16 p )
2011-04-03 15:21:06 +02:00
{
v3s16 blockpos = getNodeBlockPos ( p ) ;
v3s16 p_rel = p - blockpos * MAP_BLOCKSIZE ;
MapBlock * block = getBlockNoCreateNoEx ( blockpos ) ;
2011-10-17 00:04:22 +02:00
if ( ! block ) {
infostream < < " Map::getNodeMetadata(): Need to emerge "
< < PP ( blockpos ) < < std : : endl ;
block = emergeBlock ( blockpos , false ) ;
}
if ( ! block )
2011-04-04 02:45:08 +02:00
{
2011-10-17 00:04:22 +02:00
infostream < < " WARNING: Map::getNodeMetadata(): Block not found "
2011-04-04 02:45:08 +02:00
< < std : : endl ;
2011-04-03 15:21:06 +02:00
return NULL ;
2011-04-04 02:45:08 +02:00
}
2012-03-19 01:08:04 +01:00
NodeMetadata * meta = block - > m_node_metadata . get ( p_rel ) ;
2011-04-03 15:21:06 +02:00
return meta ;
}
2011-04-04 02:45:08 +02:00
void Map : : setNodeMetadata ( v3s16 p , NodeMetadata * meta )
{
v3s16 blockpos = getNodeBlockPos ( p ) ;
v3s16 p_rel = p - blockpos * MAP_BLOCKSIZE ;
MapBlock * block = getBlockNoCreateNoEx ( blockpos ) ;
2011-10-17 00:04:22 +02:00
if ( ! block ) {
infostream < < " Map::setNodeMetadata(): Need to emerge "
< < PP ( blockpos ) < < std : : endl ;
block = emergeBlock ( blockpos , false ) ;
}
if ( ! block )
2011-04-04 02:45:08 +02:00
{
2011-10-16 13:57:53 +02:00
infostream < < " WARNING: Map::setNodeMetadata(): Block not found "
2011-04-04 02:45:08 +02:00
< < std : : endl ;
return ;
}
2012-03-19 01:08:04 +01:00
block - > m_node_metadata . set ( p_rel , meta ) ;
2011-04-04 02:45:08 +02:00
}
void Map : : removeNodeMetadata ( v3s16 p )
{
v3s16 blockpos = getNodeBlockPos ( p ) ;
v3s16 p_rel = p - blockpos * MAP_BLOCKSIZE ;
MapBlock * block = getBlockNoCreateNoEx ( blockpos ) ;
if ( block = = NULL )
{
2011-10-16 13:57:53 +02:00
infostream < < " WARNING: Map::removeNodeMetadata(): Block not found "
2011-04-04 02:45:08 +02:00
< < std : : endl ;
return ;
}
2012-03-19 01:08:04 +01:00
block - > m_node_metadata . remove ( p_rel ) ;
2011-04-05 01:56:29 +02:00
}
2012-07-17 15:00:04 +02:00
NodeTimer Map : : getNodeTimer ( v3s16 p )
{
v3s16 blockpos = getNodeBlockPos ( p ) ;
v3s16 p_rel = p - blockpos * MAP_BLOCKSIZE ;
MapBlock * block = getBlockNoCreateNoEx ( blockpos ) ;
if ( ! block ) {
infostream < < " Map::getNodeTimer(): Need to emerge "
< < PP ( blockpos ) < < std : : endl ;
block = emergeBlock ( blockpos , false ) ;
}
if ( ! block )
{
infostream < < " WARNING: Map::getNodeTimer(): Block not found "
< < std : : endl ;
return NodeTimer ( ) ;
}
NodeTimer t = block - > m_node_timers . get ( p_rel ) ;
return t ;
}
void Map : : setNodeTimer ( v3s16 p , NodeTimer t )
{
v3s16 blockpos = getNodeBlockPos ( p ) ;
v3s16 p_rel = p - blockpos * MAP_BLOCKSIZE ;
MapBlock * block = getBlockNoCreateNoEx ( blockpos ) ;
if ( ! block ) {
infostream < < " Map::setNodeTimer(): Need to emerge "
< < PP ( blockpos ) < < std : : endl ;
block = emergeBlock ( blockpos , false ) ;
}
if ( ! block )
{
infostream < < " WARNING: Map::setNodeTimer(): Block not found "
< < std : : endl ;
return ;
}
block - > m_node_timers . set ( p_rel , t ) ;
}
void Map : : removeNodeTimer ( v3s16 p )
{
v3s16 blockpos = getNodeBlockPos ( p ) ;
v3s16 p_rel = p - blockpos * MAP_BLOCKSIZE ;
MapBlock * block = getBlockNoCreateNoEx ( blockpos ) ;
if ( block = = NULL )
{
infostream < < " WARNING: Map::removeNodeTimer(): Block not found "
< < std : : endl ;
return ;
}
block - > m_node_timers . remove ( p_rel ) ;
}
2010-11-27 00:02:21 +01:00
/*
ServerMap
*/
2013-01-23 04:32:30 +01:00
ServerMap : : ServerMap ( std : : string savedir , IGameDef * gamedef , EmergeManager * emerge ) :
2011-11-13 23:19:48 +01:00
Map ( dout_server , gamedef ) ,
2011-04-25 23:23:38 +02:00
m_seed ( 0 ) ,
2011-09-03 01:07:14 +02:00
m_map_metadata_changed ( true ) ,
m_database ( NULL ) ,
m_database_read ( NULL ) ,
m_database_write ( NULL )
2010-11-27 00:02:21 +01:00
{
2012-03-11 03:15:45 +01:00
verbosestream < < __FUNCTION_NAME < < std : : endl ;
2011-06-25 03:25:14 +02:00
2013-01-23 04:32:30 +01:00
m_emerge = emerge ;
m_mgparams = m_emerge - > getParamsFromSettings ( g_settings ) ;
2013-01-06 20:40:24 +01:00
if ( ! m_mgparams )
m_mgparams = new MapgenV6Params ( ) ;
2013-02-17 07:47:49 +01:00
2013-01-06 20:40:24 +01:00
m_seed = m_mgparams - > seed ;
2011-10-12 12:53:38 +02:00
if ( g_settings - > get ( " fixed_map_seed " ) . empty ( ) )
2011-08-31 11:24:50 +02:00
{
2013-01-23 16:13:04 +01:00
m_seed = ( ( ( u64 ) ( myrand ( ) & 0xffff ) < < 0 )
| ( ( u64 ) ( myrand ( ) & 0xffff ) < < 16 )
| ( ( u64 ) ( myrand ( ) & 0xffff ) < < 32 )
| ( ( u64 ) ( myrand ( ) & 0xffff ) < < 48 ) ) ;
2013-01-06 20:40:24 +01:00
m_mgparams - > seed = m_seed ;
2011-08-31 11:24:50 +02:00
}
2011-01-30 00:44:54 +01:00
2011-01-16 18:32:14 +01:00
/*
Experimental and debug stuff
*/
2011-06-25 03:25:14 +02:00
2011-01-16 18:32:14 +01:00
{
}
2011-06-25 03:25:14 +02:00
2011-01-16 18:32:14 +01:00
/*
Try to load map ; if not found , create a new one .
*/
2010-11-27 00:02:21 +01:00
m_savedir = savedir ;
m_map_saving_enabled = false ;
2011-06-25 03:25:14 +02:00
2010-11-27 00:02:21 +01:00
try
{
// If directory exists, check contents and load if possible
if ( fs : : PathExists ( m_savedir ) )
{
// If directory is empty, it is safe to save into it.
if ( fs : : GetDirListing ( m_savedir ) . size ( ) = = 0 )
{
2012-03-11 03:15:45 +01:00
infostream < < " ServerMap: Empty save directory is valid. "
2010-11-27 00:02:21 +01:00
< < std : : endl ;
m_map_saving_enabled = true ;
}
else
{
2011-04-10 21:50:31 +02:00
try {
2011-04-10 23:06:07 +02:00
// Load map metadata (seed, chunksize)
loadMapMeta ( ) ;
2011-06-25 03:25:14 +02:00
}
2013-01-06 20:40:24 +01:00
catch ( SettingNotFoundException & e ) {
infostream < < " ServerMap: Some metadata not found. "
< < " Using default settings. " < < std : : endl ;
}
2011-06-25 03:25:14 +02:00
catch ( FileNotGoodException & e ) {
2011-10-16 13:57:53 +02:00
infostream < < " WARNING: Could not load map metadata "
2011-06-25 03:25:14 +02:00
//<<" Disabling chunk-based generator."
< < std : : endl ;
//m_chunksize = 0;
}
2011-04-10 23:06:07 +02:00
2012-03-11 03:15:45 +01:00
infostream < < " ServerMap: Successfully loaded map "
< < " metadata from " < < savedir
2011-02-11 18:55:42 +01:00
< < " , assuming valid save directory. "
2012-03-11 03:15:45 +01:00
< < " seed= " < < m_seed < < " . "
2010-11-27 00:02:21 +01:00
< < std : : endl ;
m_map_saving_enabled = true ;
// Map loaded, not creating new one
return ;
}
}
// If directory doesn't exist, it is safe to save to it
else {
m_map_saving_enabled = true ;
}
}
catch ( std : : exception & e )
{
2012-03-11 03:15:45 +01:00
infostream < < " WARNING: ServerMap: Failed to load map from " < < savedir
2010-11-27 00:02:21 +01:00
< < " , exception: " < < e . what ( ) < < std : : endl ;
2011-10-16 13:57:53 +02:00
infostream < < " Please remove the map or fix it. " < < std : : endl ;
infostream < < " WARNING: Map saving will be disabled. " < < std : : endl ;
2010-11-27 00:02:21 +01:00
}
2011-10-16 13:57:53 +02:00
infostream < < " Initializing new map. " < < std : : endl ;
2011-06-25 03:25:14 +02:00
2010-11-27 00:02:21 +01:00
// Create zero sector
emergeSector ( v2s16 ( 0 , 0 ) ) ;
// Initially write whole map
2011-11-27 23:45:34 +01:00
save ( MOD_STATE_CLEAN ) ;
2010-11-27 00:02:21 +01:00
}
ServerMap : : ~ ServerMap ( )
{
2012-03-11 03:15:45 +01:00
verbosestream < < __FUNCTION_NAME < < std : : endl ;
2011-06-25 03:25:14 +02:00
2010-11-27 00:02:21 +01:00
try
{
if ( m_map_saving_enabled )
{
// Save only changed parts
2011-11-27 23:45:34 +01:00
save ( MOD_STATE_WRITE_AT_UNLOAD ) ;
2012-03-11 03:15:45 +01:00
infostream < < " ServerMap: Saved map to " < < m_savedir < < std : : endl ;
2010-11-27 00:02:21 +01:00
}
else
{
2012-03-11 03:15:45 +01:00
infostream < < " ServerMap: Map not saved " < < std : : endl ;
2010-11-27 00:02:21 +01:00
}
}
catch ( std : : exception & e )
{
2012-03-11 03:15:45 +01:00
infostream < < " ServerMap: Failed to save map to " < < m_savedir
2010-11-27 00:02:21 +01:00
< < " , exception: " < < e . what ( ) < < std : : endl ;
}
2011-06-25 03:25:14 +02:00
2011-09-03 01:07:14 +02:00
/*
Close database if it was opened
*/
if ( m_database_read )
sqlite3_finalize ( m_database_read ) ;
if ( m_database_write )
sqlite3_finalize ( m_database_write ) ;
if ( m_database )
sqlite3_close ( m_database ) ;
2011-06-25 03:25:14 +02:00
#if 0
2011-04-03 11:14:23 +02:00
/*
Free all MapChunks
*/
core : : map < v2s16 , MapChunk * > : : Iterator i = m_chunks . getIterator ( ) ;
for ( ; i . atEnd ( ) = = false ; i + + )
{
MapChunk * chunk = i . getNode ( ) - > getValue ( ) ;
delete chunk ;
}
2011-06-25 03:25:14 +02:00
# endif
2010-11-27 00:02:21 +01:00
}
2013-02-17 08:46:08 +01:00
bool ServerMap : : initBlockMake ( BlockMakeData * data , v3s16 blockpos )
2011-06-25 03:25:14 +02:00
{
2013-02-17 08:46:08 +01:00
bool enable_mapgen_debug_info = m_emerge - > mapgen_debug_info ;
EMERGE_DBG_OUT ( " initBlockMake(): " PP ( blockpos ) " - " PP ( blockpos ) ) ;
2012-12-22 06:34:35 +01:00
2012-02-03 01:23:21 +01:00
//s16 chunksize = 3;
//v3s16 chunk_offset(-1,-1,-1);
//s16 chunksize = 4;
//v3s16 chunk_offset(-1,-1,-1);
s16 chunksize = 5 ;
v3s16 chunk_offset ( - 2 , - 2 , - 2 ) ;
2012-01-27 13:10:10 +01:00
v3s16 blockpos_div = getContainerPos ( blockpos - chunk_offset , chunksize ) ;
2012-01-27 00:40:57 +01:00
v3s16 blockpos_min = blockpos_div * chunksize ;
v3s16 blockpos_max = blockpos_div * chunksize + v3s16 ( 1 , 1 , 1 ) * ( chunksize - 1 ) ;
2012-01-27 13:10:10 +01:00
blockpos_min + = chunk_offset ;
blockpos_max + = chunk_offset ;
2012-02-03 01:23:21 +01:00
//v3s16 extra_borders(1,1,1);
v3s16 extra_borders ( 1 , 1 , 1 ) ;
2011-07-22 00:37:05 +02:00
// Do nothing if not inside limits (+-1 because of neighbors)
2012-02-03 01:23:21 +01:00
if ( blockpos_over_limit ( blockpos_min - extra_borders ) | |
blockpos_over_limit ( blockpos_max + extra_borders ) )
2013-02-17 08:46:08 +01:00
return false ;
2012-12-22 06:34:35 +01:00
2011-06-25 03:25:14 +02:00
data - > seed = m_seed ;
2012-01-27 00:40:57 +01:00
data - > blockpos_min = blockpos_min ;
data - > blockpos_max = blockpos_max ;
data - > blockpos_requested = blockpos ;
2011-11-16 13:08:31 +01:00
data - > nodedef = m_gamedef - > ndef ( ) ;
2011-01-15 12:50:13 +01:00
2011-06-25 03:25:14 +02:00
/*
Create the whole area of this and the neighboring blocks
*/
{
//TimeTaker timer("initBlockMake() create area");
2012-12-22 06:34:35 +01:00
2012-02-03 01:23:21 +01:00
for ( s16 x = blockpos_min . X - extra_borders . X ;
x < = blockpos_max . X + extra_borders . X ; x + + )
for ( s16 z = blockpos_min . Z - extra_borders . Z ;
z < = blockpos_max . Z + extra_borders . Z ; z + + )
2010-12-25 15:04:51 +01:00
{
2012-01-27 00:40:57 +01:00
v2s16 sectorpos ( x , z ) ;
2011-06-25 03:25:14 +02:00
// Sector metadata is loaded from disk if not already loaded.
ServerMapSector * sector = createSector ( sectorpos ) ;
assert ( sector ) ;
2012-02-03 01:23:21 +01:00
for ( s16 y = blockpos_min . Y - extra_borders . Y ;
y < = blockpos_max . Y + extra_borders . Y ; y + + )
2010-12-25 15:04:51 +01:00
{
2012-01-27 00:40:57 +01:00
v3s16 p ( x , y , z ) ;
2011-07-21 16:00:08 +02:00
//MapBlock *block = createBlock(p);
2011-07-17 12:40:47 +02:00
// 1) get from memory, 2) load from disk
2011-07-21 16:00:08 +02:00
MapBlock * block = emergeBlock ( p , false ) ;
2011-07-17 12:40:47 +02:00
// 3) create a blank one
if ( block = = NULL )
2011-07-21 16:00:08 +02:00
{
block = createBlock ( p ) ;
/*
Block gets sunlight if this is true .
Refer to the map generator heuristics .
*/
2012-11-26 03:16:48 +01:00
bool ug = m_emerge - > isBlockUnderground ( p ) ;
2011-07-21 16:00:08 +02:00
block - > setIsUnderground ( ug ) ;
}
2011-01-08 13:08:48 +01:00
2011-07-17 12:40:47 +02:00
// Lighting will not be valid after make_chunk is called
2011-06-25 03:25:14 +02:00
block - > setLightingExpired ( true ) ;
// Lighting will be calculated
//block->setLightingExpired(false);
2011-01-08 13:08:48 +01:00
}
2010-12-25 15:04:51 +01:00
}
}
2012-12-22 06:34:35 +01:00
2011-01-15 12:50:13 +01:00
/*
2011-06-25 03:25:14 +02:00
Now we have a big empty area .
Make a ManualMapVoxelManipulator that contains this and the
neighboring blocks
2011-01-15 12:50:13 +01:00
*/
2012-12-22 06:34:35 +01:00
2011-07-22 00:37:05 +02:00
// The area that contains this block and it's neighbors
2012-02-03 01:23:21 +01:00
v3s16 bigarea_blocks_min = blockpos_min - extra_borders ;
v3s16 bigarea_blocks_max = blockpos_max + extra_borders ;
2012-12-22 06:34:35 +01:00
2011-06-25 17:35:32 +02:00
data - > vmanip = new ManualMapVoxelManipulator ( this ) ;
//data->vmanip->setMap(this);
2011-01-15 12:50:13 +01:00
2011-06-25 03:25:14 +02:00
// Add the area
2010-11-27 00:02:21 +01:00
{
2011-06-25 03:25:14 +02:00
//TimeTaker timer("initBlockMake() initialEmerge");
2011-06-25 17:35:32 +02:00
data - > vmanip - > initialEmerge ( bigarea_blocks_min , bigarea_blocks_max ) ;
2011-06-25 03:25:14 +02:00
}
2013-02-17 07:47:49 +01:00
// Ensure none of the blocks to be generated were marked as containing CONTENT_IGNORE
2013-02-22 04:26:21 +01:00
/* for (s16 z = blockpos_min.Z; z <= blockpos_max.Z; z++) {
2013-02-17 07:47:49 +01:00
for ( s16 y = blockpos_min . Y ; y < = blockpos_max . Y ; y + + ) {
for ( s16 x = blockpos_min . X ; x < = blockpos_max . X ; x + + ) {
core : : map < v3s16 , u8 > : : Node * n ;
n = data - > vmanip - > m_loaded_blocks . find ( v3s16 ( x , y , z ) ) ;
if ( n = = NULL )
continue ;
u8 flags = n - > getValue ( ) ;
flags & = ~ VMANIP_BLOCK_CONTAINS_CIGNORE ;
n - > setValue ( flags ) ;
}
}
2013-02-22 04:26:21 +01:00
} */
2010-12-13 02:19:12 +01:00
2011-06-25 03:25:14 +02:00
// Data is ready now.
2013-02-17 08:46:08 +01:00
return true ;
2011-06-25 03:25:14 +02:00
}
2011-01-15 00:26:29 +01:00
2012-11-26 03:16:48 +01:00
MapBlock * ServerMap : : finishBlockMake ( BlockMakeData * data ,
2011-06-25 03:25:14 +02:00
core : : map < v3s16 , MapBlock * > & changed_blocks )
{
2012-01-27 00:40:57 +01:00
v3s16 blockpos_min = data - > blockpos_min ;
v3s16 blockpos_max = data - > blockpos_max ;
v3s16 blockpos_requested = data - > blockpos_requested ;
/*infostream<<"finishBlockMake(): ("<<blockpos_requested.X<<","
< < blockpos_requested . Y < < " , "
< < blockpos_requested . Z < < " ) " < < std : : endl ; */
2011-06-25 03:25:14 +02:00
2012-02-03 01:23:21 +01:00
v3s16 extra_borders ( 1 , 1 , 1 ) ;
2013-02-17 08:46:08 +01:00
bool enable_mapgen_debug_info = m_emerge - > mapgen_debug_info ;
2011-06-26 20:53:11 +02:00
2011-10-16 13:57:53 +02:00
/*infostream<<"Resulting vmanip:"<<std::endl;
data - > vmanip . print ( infostream ) ; */
2011-11-26 14:53:52 +01:00
// Make sure affected blocks are loaded
2012-02-03 01:23:21 +01:00
for ( s16 x = blockpos_min . X - extra_borders . X ;
x < = blockpos_max . X + extra_borders . X ; x + + )
for ( s16 z = blockpos_min . Z - extra_borders . Z ;
z < = blockpos_max . Z + extra_borders . Z ; z + + )
for ( s16 y = blockpos_min . Y - extra_borders . Y ;
y < = blockpos_max . Y + extra_borders . Y ; y + + )
2011-11-26 14:53:52 +01:00
{
2012-01-27 00:40:57 +01:00
v3s16 p ( x , y , z ) ;
2011-11-26 14:53:52 +01:00
// Load from disk if not already in memory
emergeBlock ( p , false ) ;
}
2010-11-27 00:02:21 +01:00
/*
2011-06-25 03:25:14 +02:00
Blit generated stuff to map
2011-06-25 15:32:09 +02:00
NOTE : blitBackAll adds nearly everything to changed_blocks
2010-11-27 00:02:21 +01:00
*/
2011-06-25 03:25:14 +02:00
{
// 70ms @cs=8
//TimeTaker timer("finishBlockMake() blitBackAll");
2011-06-25 17:35:32 +02:00
data - > vmanip - > blitBackAll ( & changed_blocks ) ;
2011-06-25 03:25:14 +02:00
}
2011-06-26 20:53:11 +02:00
2013-02-17 08:46:08 +01:00
EMERGE_DBG_OUT ( " finishBlockMake: changed_blocks.size()= " < < changed_blocks . size ( ) ) ;
2011-06-26 20:53:11 +02:00
2010-11-29 09:52:07 +01:00
/*
2011-06-25 03:25:14 +02:00
Copy transforming liquid information
2010-11-29 09:52:07 +01:00
*/
2011-06-25 03:25:14 +02:00
while ( data - > transforming_liquid . size ( ) > 0 )
2010-11-29 09:52:07 +01:00
{
2011-06-25 03:25:14 +02:00
v3s16 p = data - > transforming_liquid . pop_front ( ) ;
m_transforming_liquid . push_back ( p ) ;
}
/*
2012-01-27 00:40:57 +01:00
Do stuff in central blocks
2010-11-27 00:02:21 +01:00
*/
2011-06-25 03:25:14 +02:00
/*
Update lighting
*/
2010-11-27 00:02:21 +01:00
{
2012-01-27 13:10:10 +01:00
#if 0
2011-06-26 01:34:36 +02:00
TimeTaker t ( " finishBlockMake lighting update " ) ;
core : : map < v3s16 , MapBlock * > lighting_update_blocks ;
2012-12-22 06:34:35 +01:00
2012-01-27 00:40:57 +01:00
// Center blocks
2012-02-03 01:23:21 +01:00
for ( s16 x = blockpos_min . X - extra_borders . X ;
x < = blockpos_max . X + extra_borders . X ; x + + )
for ( s16 z = blockpos_min . Z - extra_borders . Z ;
z < = blockpos_max . Z + extra_borders . Z ; z + + )
for ( s16 y = blockpos_min . Y - extra_borders . Y ;
y < = blockpos_max . Y + extra_borders . Y ; y + + )
2011-06-26 01:34:36 +02:00
{
2012-01-27 00:40:57 +01:00
v3s16 p ( x , y , z ) ;
MapBlock * block = getBlockNoCreateNoEx ( p ) ;
assert ( block ) ;
lighting_update_blocks . insert ( block - > getPos ( ) , block ) ;
2011-06-26 01:34:36 +02:00
}
2012-01-27 00:40:57 +01:00
2011-06-26 01:34:36 +02:00
updateLighting ( lighting_update_blocks , changed_blocks ) ;
2012-01-27 13:10:10 +01:00
# endif
2012-12-22 06:34:35 +01:00
2011-07-21 16:00:08 +02:00
/*
Set lighting to non - expired state in all of them .
This is cheating , but it is not fast enough if all of them
would actually be updated .
*/
2012-02-03 01:23:21 +01:00
for ( s16 x = blockpos_min . X - extra_borders . X ;
x < = blockpos_max . X + extra_borders . X ; x + + )
for ( s16 z = blockpos_min . Z - extra_borders . Z ;
z < = blockpos_max . Z + extra_borders . Z ; z + + )
for ( s16 y = blockpos_min . Y - extra_borders . Y ;
y < = blockpos_max . Y + extra_borders . Y ; y + + )
2011-07-21 16:00:08 +02:00
{
2012-01-27 00:40:57 +01:00
v3s16 p ( x , y , z ) ;
2011-07-21 16:00:08 +02:00
getBlockNoCreateNoEx ( p ) - > setLightingExpired ( false ) ;
}
2011-06-26 20:53:11 +02:00
2012-01-27 13:10:10 +01:00
#if 0
2011-06-26 20:53:11 +02:00
if ( enable_mapgen_debug_info = = false )
t . stop ( true ) ; // Hide output
2012-01-27 13:10:10 +01:00
# endif
2011-06-25 15:32:09 +02:00
}
2011-06-26 01:34:36 +02:00
2011-06-25 03:25:14 +02:00
/*
Go through changed blocks
*/
for ( core : : map < v3s16 , MapBlock * > : : Iterator i = changed_blocks . getIterator ( ) ;
i . atEnd ( ) = = false ; i + + )
{
MapBlock * block = i . getNode ( ) - > getValue ( ) ;
assert ( block ) ;
2010-12-22 16:58:02 +01:00
/*
2011-06-25 03:25:14 +02:00
Update day / night difference cache of the MapBlocks
2010-12-22 16:58:02 +01:00
*/
2012-03-28 23:28:48 +02:00
block - > expireDayNightDiff ( ) ;
2010-12-22 16:58:02 +01:00
/*
2011-06-25 03:25:14 +02:00
Set block as modified
2010-12-22 16:58:02 +01:00
*/
2011-11-21 12:29:16 +01:00
block - > raiseModified ( MOD_STATE_WRITE_NEEDED ,
2012-03-28 23:28:48 +02:00
" finishBlockMake expireDayNightDiff " ) ;
2011-06-25 03:25:14 +02:00
}
2011-01-25 23:41:06 +01:00
2011-06-25 03:25:14 +02:00
/*
2012-01-27 00:40:57 +01:00
Set central blocks as generated
2011-06-25 03:25:14 +02:00
*/
2012-01-27 00:40:57 +01:00
for ( s16 x = blockpos_min . X ; x < = blockpos_max . X ; x + + )
for ( s16 z = blockpos_min . Z ; z < = blockpos_max . Z ; z + + )
for ( s16 y = blockpos_min . Y ; y < = blockpos_max . Y ; y + + )
{
v3s16 p ( x , y , z ) ;
MapBlock * block = getBlockNoCreateNoEx ( p ) ;
assert ( block ) ;
block - > setGenerated ( true ) ;
}
2012-12-22 06:34:35 +01:00
2011-06-25 03:25:14 +02:00
/*
Save changed parts of map
NOTE : Will be saved later .
*/
2011-11-27 23:45:34 +01:00
//save(MOD_STATE_WRITE_AT_UNLOAD);
2011-01-25 23:41:06 +01:00
2012-01-27 00:40:57 +01:00
/*infostream<<"finishBlockMake() done for ("<<blockpos_requested.X
< < " , " < < blockpos_requested . Y < < " , "
< < blockpos_requested . Z < < " ) " < < std : : endl ; */
2011-07-24 11:09:33 +02:00
#if 0
if ( enable_mapgen_debug_info )
{
/*
Analyze resulting blocks
*/
2012-01-27 00:40:57 +01:00
/*for(s16 x=blockpos_min.X-1; x<=blockpos_max.X+1; x++)
for ( s16 z = blockpos_min . Z - 1 ; z < = blockpos_max . Z + 1 ; z + + )
for ( s16 y = blockpos_min . Y - 1 ; y < = blockpos_max . Y + 1 ; y + + ) */
for ( s16 x = blockpos_min . X - 0 ; x < = blockpos_max . X + 0 ; x + + )
for ( s16 z = blockpos_min . Z - 0 ; z < = blockpos_max . Z + 0 ; z + + )
for ( s16 y = blockpos_min . Y - 0 ; y < = blockpos_max . Y + 0 ; y + + )
2011-07-24 11:09:33 +02:00
{
2012-01-27 00:40:57 +01:00
v3s16 p = v3s16 ( x , y , z ) ;
2011-07-24 11:09:33 +02:00
MapBlock * block = getBlockNoCreateNoEx ( p ) ;
char spos [ 20 ] ;
snprintf ( spos , 20 , " (%2d,%2d,%2d) " , x , y , z ) ;
2011-10-16 13:57:53 +02:00
infostream < < " Generated " < < spos < < " : "
2011-07-24 11:09:33 +02:00
< < analyze_block ( block ) < < std : : endl ;
}
}
# endif
2012-01-27 00:40:57 +01:00
MapBlock * block = getBlockNoCreateNoEx ( blockpos_requested ) ;
assert ( block ) ;
2011-06-25 03:25:14 +02:00
return block ;
}
2010-12-22 15:30:23 +01:00
2011-06-25 03:25:14 +02:00
ServerMapSector * ServerMap : : createSector ( v2s16 p2d )
{
DSTACKF ( " %s: p2d=(%d,%d) " ,
__FUNCTION_NAME ,
p2d . X , p2d . Y ) ;
2012-12-22 06:34:35 +01:00
2011-06-25 03:25:14 +02:00
/*
Check if it exists already in memory
*/
ServerMapSector * sector = ( ServerMapSector * ) getSectorNoGenerateNoEx ( p2d ) ;
if ( sector ! = NULL )
return sector ;
2012-12-22 06:34:35 +01:00
2010-11-27 00:02:21 +01:00
/*
2011-06-25 03:25:14 +02:00
Try to load it from disk ( with blocks )
*/
//if(loadSectorFull(p2d) == true)
/*
Try to load metadata from disk
2010-11-27 00:02:21 +01:00
*/
2011-09-03 01:07:14 +02:00
#if 0
2011-06-25 03:25:14 +02:00
if ( loadSectorMeta ( p2d ) = = true )
2010-11-27 00:02:21 +01:00
{
2011-06-25 03:25:14 +02:00
ServerMapSector * sector = ( ServerMapSector * ) getSectorNoGenerateNoEx ( p2d ) ;
if ( sector = = NULL )
2010-11-27 00:02:21 +01:00
{
2011-10-16 13:57:53 +02:00
infostream < < " ServerMap::createSector(): loadSectorFull didn't make a sector " < < std : : endl ;
2011-06-25 03:25:14 +02:00
throw InvalidPositionException ( " " ) ;
2010-11-27 00:02:21 +01:00
}
2011-06-25 03:25:14 +02:00
return sector ;
2010-11-29 11:16:17 +01:00
}
2011-09-03 01:07:14 +02:00
# endif
2011-06-25 03:25:14 +02:00
/*
Do not create over - limit
*/
if ( p2d . X < - MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
| | p2d . X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
| | p2d . Y < - MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
| | p2d . Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE )
throw InvalidPositionException ( " createSector() : pos . over limit " ) ;
/*
Generate blank sector
*/
2012-12-22 06:34:35 +01:00
2011-11-14 20:41:30 +01:00
sector = new ServerMapSector ( this , p2d , m_gamedef ) ;
2012-12-22 06:34:35 +01:00
2011-06-25 03:25:14 +02:00
// Sector position on map in nodes
v2s16 nodepos2d = p2d * MAP_BLOCKSIZE ;
2010-11-27 00:02:21 +01:00
/*
2011-06-25 03:25:14 +02:00
Insert to container
2010-11-27 00:02:21 +01:00
*/
2011-06-25 03:25:14 +02:00
m_sectors . insert ( p2d , sector ) ;
2012-12-22 06:34:35 +01:00
2011-06-25 03:25:14 +02:00
return sector ;
}
2012-11-26 03:16:48 +01:00
#if 0
2011-06-25 03:25:14 +02:00
/*
This is a quick - hand function for calling makeBlock ( ) .
*/
MapBlock * ServerMap : : generateBlock (
v3s16 p ,
core : : map < v3s16 , MapBlock * > & modified_blocks
)
{
DSTACKF ( " %s: p=(%d,%d,%d) " , __FUNCTION_NAME , p . X , p . Y , p . Z ) ;
2012-12-22 06:34:35 +01:00
2011-10-16 13:57:53 +02:00
/*infostream<<"generateBlock(): "
2011-06-25 03:25:14 +02:00
< < " ( " < < p . X < < " , " < < p . Y < < " , " < < p . Z < < " ) "
< < std : : endl ; */
2012-12-22 06:34:35 +01:00
2011-10-12 12:53:38 +02:00
bool enable_mapgen_debug_info = g_settings - > getBool ( " enable_mapgen_debug_info " ) ;
2011-06-26 20:53:11 +02:00
2011-06-25 15:32:09 +02:00
TimeTaker timer ( " generateBlock " ) ;
2012-12-22 06:34:35 +01:00
2011-06-25 03:25:14 +02:00
//MapBlock *block = original_dummy;
2012-12-22 06:34:35 +01:00
2011-06-25 03:25:14 +02:00
v2s16 p2d ( p . X , p . Z ) ;
v2s16 p2d_nodes = p2d * MAP_BLOCKSIZE ;
2012-12-22 06:34:35 +01:00
2011-06-25 03:25:14 +02:00
/*
Do not generate over - limit
*/
if ( blockpos_over_limit ( p ) )
{
2011-10-16 13:57:53 +02:00
infostream < < __FUNCTION_NAME < < " : Block position over limit " < < std : : endl ;
2011-06-25 03:25:14 +02:00
throw InvalidPositionException ( " generateBlock() : pos . over limit " ) ;
}
/*
Create block make data
*/
2012-11-26 03:16:48 +01:00
BlockMakeData data ;
2011-06-25 03:25:14 +02:00
initBlockMake ( & data , p ) ;
/*
Generate block
*/
{
2011-06-25 17:12:41 +02:00
TimeTaker t ( " mapgen::make_block() " ) ;
2012-11-26 03:16:48 +01:00
mapgen - > makeChunk ( & data ) ;
//mapgen::make_block(&data);
2011-06-26 20:53:11 +02:00
if ( enable_mapgen_debug_info = = false )
t . stop ( true ) ; // Hide output
2011-06-25 03:25:14 +02:00
}
/*
Blit data back on map , update lighting , add mobs and whatever this does
*/
finishBlockMake ( & data , modified_blocks ) ;
/*
Get central block
*/
MapBlock * block = getBlockNoCreateNoEx ( p ) ;
#if 0
/*
Check result
*/
2011-07-22 00:37:05 +02:00
if ( block )
2011-06-25 03:25:14 +02:00
{
2011-07-22 00:37:05 +02:00
bool erroneus_content = false ;
for ( s16 z0 = 0 ; z0 < MAP_BLOCKSIZE ; z0 + + )
for ( s16 y0 = 0 ; y0 < MAP_BLOCKSIZE ; y0 + + )
for ( s16 x0 = 0 ; x0 < MAP_BLOCKSIZE ; x0 + + )
{
v3s16 p ( x0 , y0 , z0 ) ;
MapNode n = block - > getNode ( p ) ;
2011-07-23 15:55:26 +02:00
if ( n . getContent ( ) = = CONTENT_IGNORE )
2011-07-22 00:37:05 +02:00
{
2011-10-16 13:57:53 +02:00
infostream < < " CONTENT_IGNORE at "
2011-07-22 00:37:05 +02:00
< < " ( " < < p . X < < " , " < < p . Y < < " , " < < p . Z < < " ) "
< < std : : endl ;
erroneus_content = true ;
assert ( 0 ) ;
}
}
if ( erroneus_content )
2011-06-25 03:25:14 +02:00
{
assert ( 0 ) ;
}
}
# endif
2011-01-30 00:44:54 +01:00
#if 0
/*
2011-06-25 03:25:14 +02:00
Generate a completely empty block
2011-01-30 00:44:54 +01:00
*/
2011-07-22 00:37:05 +02:00
if ( block )
2011-06-25 03:25:14 +02:00
{
2011-07-22 00:37:05 +02:00
for ( s16 z0 = 0 ; z0 < MAP_BLOCKSIZE ; z0 + + )
for ( s16 x0 = 0 ; x0 < MAP_BLOCKSIZE ; x0 + + )
2011-06-25 03:25:14 +02:00
{
2011-07-22 00:37:05 +02:00
for ( s16 y0 = 0 ; y0 < MAP_BLOCKSIZE ; y0 + + )
{
MapNode n ;
2011-11-16 00:15:32 +01:00
n . setContent ( CONTENT_AIR ) ;
2011-07-22 00:37:05 +02:00
block - > setNode ( v3s16 ( x0 , y0 , z0 ) , n ) ;
}
2011-06-25 03:25:14 +02:00
}
}
2011-01-30 00:44:54 +01:00
# endif
2011-06-26 20:53:11 +02:00
if ( enable_mapgen_debug_info = = false )
timer . stop ( true ) ; // Hide output
2011-01-30 00:44:54 +01:00
return block ;
}
2012-11-26 03:16:48 +01:00
# endif
2011-01-30 00:44:54 +01:00
2011-02-01 02:06:02 +01:00
MapBlock * ServerMap : : createBlock ( v3s16 p )
{
2011-05-16 22:47:50 +02:00
DSTACKF ( " %s: p=(%d,%d,%d) " ,
2011-02-01 02:06:02 +01:00
__FUNCTION_NAME , p . X , p . Y , p . Z ) ;
2012-12-22 06:34:35 +01:00
2011-02-15 00:02:31 +01:00
/*
Do not create over - limit
*/
if ( p . X < - MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
| | p . X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
| | p . Y < - MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
| | p . Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
| | p . Z < - MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
| | p . Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE )
throw InvalidPositionException ( " createBlock() : pos . over limit " ) ;
2012-12-22 06:34:35 +01:00
2011-02-01 02:06:02 +01:00
v2s16 p2d ( p . X , p . Z ) ;
s16 block_y = p . Y ;
/*
This will create or load a sector if not found in memory .
If block exists on disk , it will be loaded .
NOTE : On old save formats , this will be slow , as it generates
lighting on blocks for them .
*/
ServerMapSector * sector ;
try {
sector = ( ServerMapSector * ) createSector ( p2d ) ;
assert ( sector - > getId ( ) = = MAPSECTOR_SERVER ) ;
}
2011-02-15 00:02:31 +01:00
catch ( InvalidPositionException & e )
2011-02-01 02:06:02 +01:00
{
2011-10-16 13:57:53 +02:00
infostream < < " createBlock: createSector() failed " < < std : : endl ;
2011-02-01 02:06:02 +01:00
throw e ;
2011-02-15 00:02:31 +01:00
}
/*
NOTE : This should not be done , or at least the exception
should not be passed on as std : : exception , because it
won ' t be catched at all .
*/
/*catch(std::exception &e)
2011-02-01 02:06:02 +01:00
{
2011-10-16 13:57:53 +02:00
infostream < < " createBlock: createSector() failed: "
2011-02-01 02:06:02 +01:00
< < e . what ( ) < < std : : endl ;
throw e ;
2011-02-15 00:02:31 +01:00
} */
2011-02-01 02:06:02 +01:00
/*
Try to get a block from the sector
*/
MapBlock * block = sector - > getBlockNoCreateNoEx ( block_y ) ;
if ( block )
2011-06-25 03:25:14 +02:00
{
if ( block - > isDummy ( ) )
block - > unDummify ( ) ;
2011-02-01 02:06:02 +01:00
return block ;
2011-06-25 03:25:14 +02:00
}
2011-02-01 02:06:02 +01:00
// Create blank
block = sector - > createBlankBlock ( block_y ) ;
2012-11-26 03:16:48 +01:00
2011-02-01 02:06:02 +01:00
return block ;
}
2012-11-26 03:16:48 +01:00
MapBlock * ServerMap : : emergeBlock ( v3s16 p , bool create_blank )
2011-01-30 00:44:54 +01:00
{
2012-11-26 03:16:48 +01:00
DSTACKF ( " %s: p=(%d,%d,%d), create_blank=%d " ,
2011-01-30 00:44:54 +01:00
__FUNCTION_NAME ,
2012-11-26 03:16:48 +01:00
p . X , p . Y , p . Z , create_blank ) ;
2012-12-22 06:34:35 +01:00
2011-07-01 20:04:40 +02:00
{
MapBlock * block = getBlockNoCreateNoEx ( p ) ;
2011-07-21 16:00:08 +02:00
if ( block & & block - > isDummy ( ) = = false )
2011-07-01 20:04:40 +02:00
return block ;
}
{
MapBlock * block = loadBlock ( p ) ;
if ( block )
return block ;
}
2012-11-26 03:16:48 +01:00
if ( create_blank ) {
ServerMapSector * sector = createSector ( v2s16 ( p . X , p . Z ) ) ;
MapBlock * block = sector - > createBlankBlock ( p . Y ) ;
return block ;
}
/*if(allow_generate)
2011-07-01 20:04:40 +02:00
{
core : : map < v3s16 , MapBlock * > modified_blocks ;
MapBlock * block = generateBlock ( p , modified_blocks ) ;
if ( block )
{
MapEditEvent event ;
event . type = MEET_OTHER ;
event . p = p ;
// Copy modified_blocks to event
for ( core : : map < v3s16 , MapBlock * > : : Iterator
i = modified_blocks . getIterator ( ) ;
i . atEnd ( ) = = false ; i + + )
{
event . modified_blocks . insert ( i . getNode ( ) - > getKey ( ) , false ) ;
}
// Queue event
dispatchEvent ( & event ) ;
2012-12-22 06:34:35 +01:00
2011-07-01 20:04:40 +02:00
return block ;
}
2012-11-26 03:16:48 +01:00
} */
2011-07-01 20:04:40 +02:00
2011-06-25 03:25:14 +02:00
return NULL ;
}
2011-02-23 01:49:57 +01:00
s16 ServerMap : : findGroundLevel ( v2s16 p2d )
{
2011-06-25 03:25:14 +02:00
#if 0
2011-02-23 01:49:57 +01:00
/*
Uh , just do something random . . .
*/
// Find existing map from top to down
s16 max = 63 ;
s16 min = - 64 ;
v3s16 p ( p2d . X , max , p2d . Y ) ;
for ( ; p . Y > min ; p . Y - - )
{
MapNode n = getNodeNoEx ( p ) ;
2011-07-23 15:55:26 +02:00
if ( n . getContent ( ) ! = CONTENT_IGNORE )
2011-02-23 01:49:57 +01:00
break ;
}
if ( p . Y = = min )
goto plan_b ;
// If this node is not air, go to plan b
2011-07-23 15:55:26 +02:00
if ( getNodeNoEx ( p ) . getContent ( ) ! = CONTENT_AIR )
2011-02-23 01:49:57 +01:00
goto plan_b ;
// Search existing walkable and return it
for ( ; p . Y > min ; p . Y - - )
{
MapNode n = getNodeNoEx ( p ) ;
2011-07-23 15:55:26 +02:00
if ( content_walkable ( n . d ) & & n . getContent ( ) ! = CONTENT_IGNORE )
2011-02-23 01:49:57 +01:00
return p . Y ;
}
2011-06-25 03:25:14 +02:00
2011-02-23 01:49:57 +01:00
// Move to plan b
plan_b :
2011-06-25 03:25:14 +02:00
# endif
2011-02-23 01:49:57 +01:00
/*
2011-06-25 03:25:14 +02:00
Determine from map generator noise functions
2011-02-23 01:49:57 +01:00
*/
2012-12-22 06:34:35 +01:00
2012-11-26 03:16:48 +01:00
s16 level = m_emerge - > getGroundLevelAtPoint ( p2d ) ;
2011-06-25 03:25:14 +02:00
return level ;
//double level = base_rock_level_2d(m_seed, p2d) + AVERAGE_MUD_AMOUNT;
//return (s16)level;
2011-02-23 01:49:57 +01:00
}
2011-09-03 01:07:14 +02:00
void ServerMap : : createDatabase ( ) {
int e ;
assert ( m_database ) ;
e = sqlite3_exec ( m_database ,
" CREATE TABLE IF NOT EXISTS `blocks` ( "
" `pos` INT NOT NULL PRIMARY KEY, "
" `data` BLOB "
" ); "
, NULL , NULL , NULL ) ;
if ( e = = SQLITE_ABORT )
throw FileNotGoodException ( " Could not create database structure " ) ;
else
2012-03-11 03:15:45 +01:00
infostream < < " ServerMap: Database structure was created " ;
2011-09-03 01:07:14 +02:00
}
void ServerMap : : verifyDatabase ( ) {
if ( m_database )
return ;
2012-12-22 06:34:35 +01:00
2011-09-03 01:07:14 +02:00
{
2011-10-16 15:16:47 +02:00
std : : string dbp = m_savedir + DIR_DELIM + " map.sqlite " ;
2011-09-03 01:07:14 +02:00
bool needs_create = false ;
int d ;
2012-12-22 06:34:35 +01:00
2011-09-03 01:07:14 +02:00
/*
Open the database connection
*/
2012-12-22 06:34:35 +01:00
2011-09-03 01:07:14 +02:00
createDirs ( m_savedir ) ;
2012-12-22 06:34:35 +01:00
2011-09-03 01:07:14 +02:00
if ( ! fs : : PathExists ( dbp ) )
needs_create = true ;
2012-12-22 06:34:35 +01:00
2011-09-03 01:07:14 +02:00
d = sqlite3_open_v2 ( dbp . c_str ( ) , & m_database , SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE , NULL ) ;
if ( d ! = SQLITE_OK ) {
2011-10-16 13:57:53 +02:00
infostream < < " WARNING: Database failed to open: " < < sqlite3_errmsg ( m_database ) < < std : : endl ;
2011-09-03 01:07:14 +02:00
throw FileNotGoodException ( " Cannot open database file " ) ;
}
2012-12-22 06:34:35 +01:00
2011-09-03 01:07:14 +02:00
if ( needs_create )
createDatabase ( ) ;
2012-12-22 06:34:35 +01:00
2011-09-03 01:07:14 +02:00
d = sqlite3_prepare ( m_database , " SELECT `data` FROM `blocks` WHERE `pos`=? LIMIT 1 " , - 1 , & m_database_read , NULL ) ;
if ( d ! = SQLITE_OK ) {
2011-10-16 13:57:53 +02:00
infostream < < " WARNING: Database read statment failed to prepare: " < < sqlite3_errmsg ( m_database ) < < std : : endl ;
2011-09-03 01:07:14 +02:00
throw FileNotGoodException ( " Cannot prepare read statement " ) ;
}
2012-12-22 06:34:35 +01:00
2011-09-03 01:07:14 +02:00
d = sqlite3_prepare ( m_database , " REPLACE INTO `blocks` VALUES(?, ?) " , - 1 , & m_database_write , NULL ) ;
if ( d ! = SQLITE_OK ) {
2011-10-16 13:57:53 +02:00
infostream < < " WARNING: Database write statment failed to prepare: " < < sqlite3_errmsg ( m_database ) < < std : : endl ;
2011-09-03 01:07:14 +02:00
throw FileNotGoodException ( " Cannot prepare write statement " ) ;
}
2012-12-22 06:34:35 +01:00
2011-10-17 23:01:50 +02:00
d = sqlite3_prepare ( m_database , " SELECT `pos` FROM `blocks` " , - 1 , & m_database_list , NULL ) ;
if ( d ! = SQLITE_OK ) {
infostream < < " WARNING: Database list statment failed to prepare: " < < sqlite3_errmsg ( m_database ) < < std : : endl ;
throw FileNotGoodException ( " Cannot prepare read statement " ) ;
}
2012-12-22 06:34:35 +01:00
2012-03-11 03:15:45 +01:00
infostream < < " ServerMap: Database opened " < < std : : endl ;
2011-09-03 01:07:14 +02:00
}
}
bool ServerMap : : loadFromFolders ( ) {
2011-10-16 15:16:47 +02:00
if ( ! m_database & & ! fs : : PathExists ( m_savedir + DIR_DELIM + " map.sqlite " ) )
2011-09-03 01:07:14 +02:00
return true ;
return false ;
}
2011-09-04 23:01:28 +02:00
sqlite3_int64 ServerMap : : getBlockAsInteger ( const v3s16 pos ) {
return ( sqlite3_int64 ) pos . Z * 16777216 +
( sqlite3_int64 ) pos . Y * 4096 + ( sqlite3_int64 ) pos . X ;
2011-09-03 01:07:14 +02:00
}
2011-05-18 17:48:27 +02:00
void ServerMap : : createDirs ( std : : string path )
2010-11-27 00:02:21 +01:00
{
2011-05-18 17:48:27 +02:00
if ( fs : : CreateAllDirs ( path ) = = false )
2010-11-27 00:02:21 +01:00
{
m_dout < < DTIME < < " ServerMap: Failed to create directory "
< < " \" " < < path < < " \" " < < std : : endl ;
throw BaseException ( " ServerMap failed to create directory " ) ;
}
}
2011-05-18 17:48:27 +02:00
std : : string ServerMap : : getSectorDir ( v2s16 pos , int layout )
2010-11-27 00:02:21 +01:00
{
char cc [ 9 ] ;
2011-05-18 17:48:27 +02:00
switch ( layout )
{
case 1 :
snprintf ( cc , 9 , " %.4x%.4x " ,
( unsigned int ) pos . X & 0xffff ,
( unsigned int ) pos . Y & 0xffff ) ;
2010-11-27 00:02:21 +01:00
2011-10-16 15:16:47 +02:00
return m_savedir + DIR_DELIM + " sectors " + DIR_DELIM + cc ;
2011-05-18 17:48:27 +02:00
case 2 :
2011-10-16 15:16:47 +02:00
snprintf ( cc , 9 , " %.3x " DIR_DELIM " %.3x " ,
2011-05-18 17:48:27 +02:00
( unsigned int ) pos . X & 0xfff ,
( unsigned int ) pos . Y & 0xfff ) ;
2010-11-27 00:02:21 +01:00
2011-10-16 15:16:47 +02:00
return m_savedir + DIR_DELIM + " sectors2 " + DIR_DELIM + cc ;
2011-05-18 17:48:27 +02:00
default :
assert ( false ) ;
}
2010-11-27 00:02:21 +01:00
}
v2s16 ServerMap : : getSectorPos ( std : : string dirname )
{
unsigned int x , y ;
2011-05-18 17:48:27 +02:00
int r ;
2011-10-16 15:16:47 +02:00
size_t spos = dirname . rfind ( DIR_DELIM_C ) + 1 ;
2011-05-18 17:48:27 +02:00
assert ( spos ! = std : : string : : npos ) ;
if ( dirname . size ( ) - spos = = 8 )
{
// Old layout
r = sscanf ( dirname . substr ( spos ) . c_str ( ) , " %4x%4x " , & x , & y ) ;
}
else if ( dirname . size ( ) - spos = = 3 )
{
// New layout
2011-10-16 15:16:47 +02:00
r = sscanf ( dirname . substr ( spos - 4 ) . c_str ( ) , " %3x " DIR_DELIM " %3x " , & x , & y ) ;
2011-05-18 17:48:27 +02:00
// Sign-extend the 12 bit values up to 16 bits...
if ( x & 0x800 ) x | = 0xF000 ;
if ( y & 0x800 ) y | = 0xF000 ;
}
else
{
assert ( false ) ;
}
assert ( r = = 2 ) ;
2010-11-27 00:02:21 +01:00
v2s16 pos ( ( s16 ) x , ( s16 ) y ) ;
return pos ;
}
v3s16 ServerMap : : getBlockPos ( std : : string sectordir , std : : string blockfile )
{
v2s16 p2d = getSectorPos ( sectordir ) ;
if ( blockfile . size ( ) ! = 4 ) {
throw InvalidFilenameException ( " Invalid block filename " ) ;
}
unsigned int y ;
int r = sscanf ( blockfile . c_str ( ) , " %4x " , & y ) ;
if ( r ! = 1 )
throw InvalidFilenameException ( " Invalid block filename " ) ;
return v3s16 ( p2d . X , y , p2d . Y ) ;
}
2011-06-25 03:25:14 +02:00
std : : string ServerMap : : getBlockFilename ( v3s16 p )
{
char cc [ 5 ] ;
snprintf ( cc , 5 , " %.4x " , ( unsigned int ) p . Y & 0xffff ) ;
return cc ;
}
2011-11-27 23:45:34 +01:00
void ServerMap : : save ( ModifiedState save_level )
2010-11-27 00:02:21 +01:00
{
DSTACK ( __FUNCTION_NAME ) ;
if ( m_map_saving_enabled = = false )
{
2011-10-16 13:57:53 +02:00
infostream < < " WARNING: Not saving map, saving disabled. " < < std : : endl ;
2010-11-27 00:02:21 +01:00
return ;
}
2012-12-22 06:34:35 +01:00
2011-11-27 23:45:34 +01:00
if ( save_level = = MOD_STATE_CLEAN )
2011-10-16 13:57:53 +02:00
infostream < < " ServerMap: Saving whole map, this can take time. "
2010-11-27 00:02:21 +01:00
< < std : : endl ;
2012-12-22 06:34:35 +01:00
2011-11-27 23:45:34 +01:00
if ( m_map_metadata_changed | | save_level = = MOD_STATE_CLEAN )
2011-04-25 23:23:38 +02:00
{
saveMapMeta ( ) ;
}
2011-04-10 21:50:31 +02:00
2011-11-21 12:35:32 +01:00
// Profile modified reasons
Profiler modprofiler ;
2012-12-22 06:34:35 +01:00
2010-11-27 00:02:21 +01:00
u32 sector_meta_count = 0 ;
u32 block_count = 0 ;
2011-06-05 17:57:36 +02:00
u32 block_count_all = 0 ; // Number of blocks in memory
2012-12-22 06:34:35 +01:00
2011-11-21 13:13:28 +01:00
// Don't do anything with sqlite unless something is really saved
bool save_started = false ;
2010-11-27 00:02:21 +01:00
core : : map < v2s16 , MapSector * > : : Iterator i = m_sectors . getIterator ( ) ;
for ( ; i . atEnd ( ) = = false ; i + + )
{
ServerMapSector * sector = ( ServerMapSector * ) i . getNode ( ) - > getValue ( ) ;
assert ( sector - > getId ( ) = = MAPSECTOR_SERVER ) ;
2012-12-22 06:34:35 +01:00
2011-11-27 23:45:34 +01:00
if ( sector - > differs_from_disk | | save_level = = MOD_STATE_CLEAN )
2010-11-27 00:02:21 +01:00
{
2011-02-05 13:55:16 +01:00
saveSectorMeta ( sector ) ;
sector_meta_count + + ;
2010-11-27 00:02:21 +01:00
}
2011-02-05 13:55:16 +01:00
core : : list < MapBlock * > blocks ;
sector - > getBlocks ( blocks ) ;
core : : list < MapBlock * > : : Iterator j ;
2012-12-22 06:34:35 +01:00
2011-02-05 13:55:16 +01:00
for ( j = blocks . begin ( ) ; j ! = blocks . end ( ) ; j + + )
2010-11-27 00:02:21 +01:00
{
2011-02-05 13:55:16 +01:00
MapBlock * block = * j ;
2012-12-22 06:34:35 +01:00
2011-06-05 17:57:36 +02:00
block_count_all + + ;
2013-02-26 07:57:59 +01:00
if ( block - > getModified ( ) > = ( u32 ) save_level )
2010-11-27 00:02:21 +01:00
{
2011-11-21 13:13:28 +01:00
// Lazy beginSave()
if ( ! save_started ) {
beginSave ( ) ;
save_started = true ;
}
2011-11-21 12:35:32 +01:00
modprofiler . add ( block - > getModifiedReason ( ) , 1 ) ;
2011-11-21 13:13:28 +01:00
2011-02-05 13:55:16 +01:00
saveBlock ( block ) ;
block_count + + ;
2011-10-16 13:57:53 +02:00
/*infostream<<"ServerMap: Written block ("
2011-02-05 13:55:16 +01:00
< < block - > getPos ( ) . X < < " , "
< < block - > getPos ( ) . Y < < " , "
< < block - > getPos ( ) . Z < < " ) "
< < std : : endl ; */
2010-11-27 00:02:21 +01:00
}
}
}
2011-11-21 13:13:28 +01:00
if ( save_started )
endSave ( ) ;
2010-11-27 00:02:21 +01:00
/*
Only print if something happened or saved whole map
*/
2011-11-27 23:45:34 +01:00
if ( save_level = = MOD_STATE_CLEAN | | sector_meta_count ! = 0
2010-12-21 01:25:47 +01:00
| | block_count ! = 0 )
2010-11-27 00:02:21 +01:00
{
2011-10-16 13:57:53 +02:00
infostream < < " ServerMap: Written: "
2010-11-27 00:02:21 +01:00
< < sector_meta_count < < " sector metadata files, "
2010-12-21 01:25:47 +01:00
< < block_count < < " block files "
2011-06-05 17:57:36 +02:00
< < " , " < < block_count_all < < " blocks in memory. "
2010-11-27 00:02:21 +01:00
< < std : : endl ;
2011-11-21 12:35:32 +01:00
PrintInfo ( infostream ) ; // ServerMap/ClientMap:
infostream < < " Blocks modified by: " < < std : : endl ;
modprofiler . print ( infostream ) ;
2010-11-27 00:02:21 +01:00
}
}
2011-10-17 23:01:50 +02:00
static s32 unsignedToSigned ( s32 i , s32 max_positive )
{
if ( i < max_positive )
return i ;
else
return i - 2 * max_positive ;
}
// modulo of a negative number does not work consistently in C
static sqlite3_int64 pythonmodulo ( sqlite3_int64 i , sqlite3_int64 mod )
{
if ( i > = 0 )
return i % mod ;
return mod - ( ( - i ) % mod ) ;
}
v3s16 ServerMap : : getIntegerAsBlock ( sqlite3_int64 i )
{
s32 x = unsignedToSigned ( pythonmodulo ( i , 4096 ) , 2048 ) ;
i = ( i - x ) / 4096 ;
s32 y = unsignedToSigned ( pythonmodulo ( i , 4096 ) , 2048 ) ;
i = ( i - y ) / 4096 ;
s32 z = unsignedToSigned ( pythonmodulo ( i , 4096 ) , 2048 ) ;
return v3s16 ( x , y , z ) ;
}
void ServerMap : : listAllLoadableBlocks ( core : : list < v3s16 > & dst )
{
if ( loadFromFolders ( ) ) {
errorstream < < " Map::listAllLoadableBlocks(): Result will be missing "
< < " all blocks that are stored in flat files " < < std : : endl ;
}
2012-12-22 06:34:35 +01:00
2011-10-17 23:01:50 +02:00
{
verifyDatabase ( ) ;
2012-12-22 06:34:35 +01:00
2011-10-17 23:01:50 +02:00
while ( sqlite3_step ( m_database_list ) = = SQLITE_ROW )
{
sqlite3_int64 block_i = sqlite3_column_int64 ( m_database_list , 0 ) ;
v3s16 p = getIntegerAsBlock ( block_i ) ;
//dstream<<"block_i="<<block_i<<" p="<<PP(p)<<std::endl;
dst . push_back ( p ) ;
}
}
}
2011-02-11 18:55:42 +01:00
void ServerMap : : saveMapMeta ( )
{
DSTACK ( __FUNCTION_NAME ) ;
2012-12-22 06:34:35 +01:00
2012-03-11 03:15:45 +01:00
/*infostream<<"ServerMap::saveMapMeta(): "
2011-06-25 03:25:14 +02:00
< < " seed= " < < m_seed
2012-03-11 03:15:45 +01:00
< < std : : endl ; */
2011-02-11 18:55:42 +01:00
2011-05-18 17:48:27 +02:00
createDirs ( m_savedir ) ;
2012-12-22 06:34:35 +01:00
2011-10-16 15:16:47 +02:00
std : : string fullpath = m_savedir + DIR_DELIM + " map_meta.txt " ;
2011-02-11 18:55:42 +01:00
std : : ofstream os ( fullpath . c_str ( ) , std : : ios_base : : binary ) ;
if ( os . good ( ) = = false )
{
2011-10-16 13:57:53 +02:00
infostream < < " ERROR: ServerMap::saveMapMeta(): "
2011-02-11 18:55:42 +01:00
< < " could not open " < < fullpath < < std : : endl ;
throw FileNotGoodException ( " Cannot open chunk metadata " ) ;
}
2012-12-22 06:34:35 +01:00
2011-02-11 18:55:42 +01:00
Settings params ;
2012-12-26 09:15:16 +01:00
2013-02-05 21:01:33 +01:00
m_emerge - > setParamsToSettings ( & params ) ;
2011-02-11 18:55:42 +01:00
params . writeLines ( os ) ;
os < < " [end_of_params] \n " ;
2012-12-22 06:34:35 +01:00
2011-04-26 00:13:30 +02:00
m_map_metadata_changed = false ;
2011-02-11 18:55:42 +01:00
}
void ServerMap : : loadMapMeta ( )
{
DSTACK ( __FUNCTION_NAME ) ;
2012-12-22 06:34:35 +01:00
2012-03-11 03:15:45 +01:00
/*infostream<<"ServerMap::loadMapMeta(): Loading map metadata"
< < std : : endl ; */
2011-02-11 18:55:42 +01:00
2011-10-16 15:16:47 +02:00
std : : string fullpath = m_savedir + DIR_DELIM + " map_meta.txt " ;
2011-02-11 18:55:42 +01:00
std : : ifstream is ( fullpath . c_str ( ) , std : : ios_base : : binary ) ;
if ( is . good ( ) = = false )
{
2011-10-16 13:57:53 +02:00
infostream < < " ERROR: ServerMap::loadMapMeta(): "
2011-02-11 18:55:42 +01:00
< < " could not open " < < fullpath < < std : : endl ;
2011-04-10 23:06:07 +02:00
throw FileNotGoodException ( " Cannot open map metadata " ) ;
2011-02-11 18:55:42 +01:00
}
Settings params ;
for ( ; ; )
{
if ( is . eof ( ) )
throw SerializationError
( " ServerMap::loadMapMeta(): [end_of_params] not found " ) ;
std : : string line ;
std : : getline ( is , line ) ;
std : : string trimmedline = trim ( line ) ;
if ( trimmedline = = " [end_of_params] " )
break ;
params . parseConfigLine ( line ) ;
}
2012-12-22 06:34:35 +01:00
2013-01-23 04:32:30 +01:00
MapgenParams * mgparams = m_emerge - > getParamsFromSettings ( & params ) ;
2013-01-06 20:40:24 +01:00
if ( mgparams ) {
if ( m_mgparams )
delete m_mgparams ;
m_mgparams = mgparams ;
m_seed = mgparams - > seed ;
} else {
if ( params . exists ( " seed " ) ) {
m_seed = params . getU64 ( " seed " ) ;
m_mgparams - > seed = m_seed ;
2012-12-26 09:15:16 +01:00
}
}
2012-03-11 03:15:45 +01:00
verbosestream < < " ServerMap::loadMapMeta(): " < < " seed= " < < m_seed < < std : : endl ;
2011-02-11 18:55:42 +01:00
}
2010-11-27 00:02:21 +01:00
void ServerMap : : saveSectorMeta ( ServerMapSector * sector )
{
DSTACK ( __FUNCTION_NAME ) ;
// Format used for writing
u8 version = SER_FMT_VER_HIGHEST ;
// Get destination
v2s16 pos = sector - > getPos ( ) ;
std : : string dir = getSectorDir ( pos ) ;
2011-05-18 17:48:27 +02:00
createDirs ( dir ) ;
2012-12-22 06:34:35 +01:00
2011-10-16 15:16:47 +02:00
std : : string fullpath = dir + DIR_DELIM + " meta " ;
2010-11-27 00:02:21 +01:00
std : : ofstream o ( fullpath . c_str ( ) , std : : ios_base : : binary ) ;
if ( o . good ( ) = = false )
2011-02-11 19:37:54 +01:00
throw FileNotGoodException ( " Cannot open sector metafile " ) ;
2010-11-27 00:02:21 +01:00
sector - > serialize ( o , version ) ;
2012-12-22 06:34:35 +01:00
2010-11-27 00:02:21 +01:00
sector - > differs_from_disk = false ;
}
2011-05-18 17:48:27 +02:00
MapSector * ServerMap : : loadSectorMeta ( std : : string sectordir , bool save_after_load )
2010-11-27 00:02:21 +01:00
{
DSTACK ( __FUNCTION_NAME ) ;
// Get destination
2011-05-18 17:48:27 +02:00
v2s16 p2d = getSectorPos ( sectordir ) ;
2011-04-10 23:19:40 +02:00
ServerMapSector * sector = NULL ;
2011-05-18 17:48:27 +02:00
2011-10-16 15:16:47 +02:00
std : : string fullpath = sectordir + DIR_DELIM + " meta " ;
2010-11-27 00:02:21 +01:00
std : : ifstream is ( fullpath . c_str ( ) , std : : ios_base : : binary ) ;
if ( is . good ( ) = = false )
2011-04-10 23:19:40 +02:00
{
// If the directory exists anyway, it probably is in some old
// format. Just go ahead and create the sector.
2011-05-18 17:48:27 +02:00
if ( fs : : PathExists ( sectordir ) )
2011-04-10 23:19:40 +02:00
{
2011-10-16 13:57:53 +02:00
/*infostream<<"ServerMap::loadSectorMeta(): Sector metafile "
2011-04-10 23:19:40 +02:00
< < fullpath < < " doesn't exist but directory does. "
< < " Continuing with a sector with no metadata. "
2011-06-26 14:48:56 +02:00
< < std : : endl ; */
2011-11-14 20:41:30 +01:00
sector = new ServerMapSector ( this , p2d , m_gamedef ) ;
2011-04-10 23:24:32 +02:00
m_sectors . insert ( p2d , sector ) ;
2011-04-10 23:19:40 +02:00
}
else
2011-05-18 17:48:27 +02:00
{
2011-04-10 23:19:40 +02:00
throw FileNotGoodException ( " Cannot open sector metafile " ) ;
2011-05-18 17:48:27 +02:00
}
2011-04-10 23:19:40 +02:00
}
else
{
sector = ServerMapSector : : deSerialize
2011-11-14 20:41:30 +01:00
( is , this , p2d , m_sectors , m_gamedef ) ;
2011-05-18 17:48:27 +02:00
if ( save_after_load )
saveSectorMeta ( sector ) ;
2011-04-10 23:19:40 +02:00
}
2012-12-22 06:34:35 +01:00
2010-11-27 00:02:21 +01:00
sector - > differs_from_disk = false ;
return sector ;
}
2011-06-25 03:25:14 +02:00
bool ServerMap : : loadSectorMeta ( v2s16 p2d )
2010-11-27 00:02:21 +01:00
{
DSTACK ( __FUNCTION_NAME ) ;
MapSector * sector = NULL ;
2011-05-18 17:48:27 +02:00
// The directory layout we're going to load from.
// 1 - original sectors/xxxxzzzz/
// 2 - new sectors2/xxx/zzz/
// If we load from anything but the latest structure, we will
// immediately save to the new one, and remove the old.
int loadlayout = 1 ;
std : : string sectordir1 = getSectorDir ( p2d , 1 ) ;
std : : string sectordir ;
if ( fs : : PathExists ( sectordir1 ) )
{
sectordir = sectordir1 ;
}
else
{
loadlayout = 2 ;
sectordir = getSectorDir ( p2d , 2 ) ;
}
2011-06-25 03:25:14 +02:00
try {
sector = loadSectorMeta ( sectordir , loadlayout ! = 2 ) ;
}
catch ( InvalidFilenameException & e )
{
return false ;
}
catch ( FileNotGoodException & e )
{
return false ;
}
catch ( std : : exception & e )
{
return false ;
}
2012-12-22 06:34:35 +01:00
2011-06-25 03:25:14 +02:00
return true ;
}
#if 0
bool ServerMap : : loadSectorFull ( v2s16 p2d )
{
DSTACK ( __FUNCTION_NAME ) ;
MapSector * sector = NULL ;
// The directory layout we're going to load from.
// 1 - original sectors/xxxxzzzz/
// 2 - new sectors2/xxx/zzz/
// If we load from anything but the latest structure, we will
// immediately save to the new one, and remove the old.
int loadlayout = 1 ;
std : : string sectordir1 = getSectorDir ( p2d , 1 ) ;
std : : string sectordir ;
if ( fs : : PathExists ( sectordir1 ) )
{
sectordir = sectordir1 ;
}
else
{
loadlayout = 2 ;
sectordir = getSectorDir ( p2d , 2 ) ;
}
2010-11-27 00:02:21 +01:00
try {
2011-05-18 17:48:27 +02:00
sector = loadSectorMeta ( sectordir , loadlayout ! = 2 ) ;
2010-11-27 00:02:21 +01:00
}
catch ( InvalidFilenameException & e )
{
return false ;
}
catch ( FileNotGoodException & e )
{
return false ;
}
catch ( std : : exception & e )
{
return false ;
}
2012-12-22 06:34:35 +01:00
2011-02-11 19:37:54 +01:00
/*
Load blocks
*/
2011-02-05 13:55:16 +01:00
std : : vector < fs : : DirListNode > list2 = fs : : GetDirListing
2011-05-18 17:48:27 +02:00
( sectordir ) ;
2011-02-05 13:55:16 +01:00
std : : vector < fs : : DirListNode > : : iterator i2 ;
for ( i2 = list2 . begin ( ) ; i2 ! = list2 . end ( ) ; i2 + + )
2010-11-27 00:02:21 +01:00
{
2011-02-05 13:55:16 +01:00
// We want files
if ( i2 - > dir )
continue ;
try {
2011-05-18 17:48:27 +02:00
loadBlock ( sectordir , i2 - > name , sector , loadlayout ! = 2 ) ;
2011-02-05 13:55:16 +01:00
}
catch ( InvalidFilenameException & e )
2010-11-27 00:02:21 +01:00
{
2011-02-05 13:55:16 +01:00
// This catches unknown crap in directory
2010-11-27 00:02:21 +01:00
}
}
2011-05-18 17:48:27 +02:00
if ( loadlayout ! = 2 )
{
2011-10-16 13:57:53 +02:00
infostream < < " Sector converted to new layout - deleting " < <
2011-05-18 17:48:27 +02:00
sectordir1 < < std : : endl ;
fs : : RecursiveDelete ( sectordir1 ) ;
}
2010-11-27 00:02:21 +01:00
return true ;
}
2011-06-25 03:25:14 +02:00
# endif
2011-05-18 17:48:27 +02:00
2011-09-03 01:07:14 +02:00
void ServerMap : : beginSave ( ) {
verifyDatabase ( ) ;
if ( sqlite3_exec ( m_database , " BEGIN; " , NULL , NULL , NULL ) ! = SQLITE_OK )
2011-10-16 13:57:53 +02:00
infostream < < " WARNING: beginSave() failed, saving might be slow. " ;
2011-09-03 01:07:14 +02:00
}
void ServerMap : : endSave ( ) {
verifyDatabase ( ) ;
if ( sqlite3_exec ( m_database , " COMMIT; " , NULL , NULL , NULL ) ! = SQLITE_OK )
2011-10-16 13:57:53 +02:00
infostream < < " WARNING: endSave() failed, map might not have saved. " ;
2011-09-03 01:07:14 +02:00
}
2010-11-27 00:02:21 +01:00
void ServerMap : : saveBlock ( MapBlock * block )
{
DSTACK ( __FUNCTION_NAME ) ;
/*
Dummy blocks are not written
*/
if ( block - > isDummy ( ) )
{
/*v3s16 p = block->getPos();
2011-10-16 13:57:53 +02:00
infostream < < " ServerMap::saveBlock(): WARNING: Not writing dummy block "
2010-11-27 00:02:21 +01:00
< < " ( " < < p . X < < " , " < < p . Y < < " , " < < p . Z < < " ) " < < std : : endl ; */
return ;
}
// Format used for writing
u8 version = SER_FMT_VER_HIGHEST ;
// Get destination
v3s16 p3d = block - > getPos ( ) ;
2012-12-22 06:34:35 +01:00
2011-09-03 01:07:14 +02:00
#if 0
2011-06-25 03:25:14 +02:00
v2s16 p2d ( p3d . X , p3d . Z ) ;
std : : string sectordir = getSectorDir ( p2d ) ;
createDirs ( sectordir ) ;
2011-10-16 15:16:47 +02:00
std : : string fullpath = sectordir + DIR_DELIM + getBlockFilename ( p3d ) ;
2010-11-27 00:02:21 +01:00
std : : ofstream o ( fullpath . c_str ( ) , std : : ios_base : : binary ) ;
if ( o . good ( ) = = false )
throw FileNotGoodException ( " Cannot open block data " ) ;
2011-09-03 01:07:14 +02:00
# endif
2010-11-27 00:02:21 +01:00
/*
[ 0 ] u8 serialization version
[ 1 ] data
*/
2012-12-22 06:34:35 +01:00
2011-09-03 01:07:14 +02:00
verifyDatabase ( ) ;
2012-12-22 06:34:35 +01:00
2011-09-03 01:07:14 +02:00
std : : ostringstream o ( std : : ios_base : : binary ) ;
2012-12-22 06:34:35 +01:00
2010-11-27 00:02:21 +01:00
o . write ( ( char * ) & version , 1 ) ;
2012-12-22 06:34:35 +01:00
2011-05-22 16:00:09 +02:00
// Write basic data
2012-01-21 00:11:44 +01:00
block - > serialize ( o , version , true ) ;
2012-12-22 06:34:35 +01:00
2011-09-03 01:07:14 +02:00
// Write block to database
2012-12-22 06:34:35 +01:00
2011-09-03 01:07:14 +02:00
std : : string tmp = o . str ( ) ;
const char * bytes = tmp . c_str ( ) ;
2012-12-22 06:34:35 +01:00
2012-10-29 03:02:51 +01:00
bool success = true ;
if ( sqlite3_bind_int64 ( m_database_write , 1 , getBlockAsInteger ( p3d ) ) ! = SQLITE_OK ) {
2011-10-16 13:57:53 +02:00
infostream < < " WARNING: Block position failed to bind: " < < sqlite3_errmsg ( m_database ) < < std : : endl ;
2012-10-29 03:02:51 +01:00
success = false ;
}
if ( sqlite3_bind_blob ( m_database_write , 2 , ( void * ) bytes , o . tellp ( ) , NULL ) ! = SQLITE_OK ) { // TODO this mught not be the right length
2011-10-16 13:57:53 +02:00
infostream < < " WARNING: Block data failed to bind: " < < sqlite3_errmsg ( m_database ) < < std : : endl ;
2012-10-29 03:02:51 +01:00
success = false ;
}
2011-09-03 01:07:14 +02:00
int written = sqlite3_step ( m_database_write ) ;
2012-10-29 03:02:51 +01:00
if ( written ! = SQLITE_DONE ) {
errorstream < < " WARNING: Block failed to save ( " < < p3d . X < < " , " < < p3d . Y < < " , " < < p3d . Z < < " ) "
< < sqlite3_errmsg ( m_database ) < < std : : endl ;
success = false ;
}
2011-09-03 01:07:14 +02:00
// Make ready for later reuse
sqlite3_reset ( m_database_write ) ;
2012-12-22 06:34:35 +01:00
2011-05-22 16:00:09 +02:00
// We just wrote it to the disk so clear modified flag
2012-10-29 03:02:51 +01:00
if ( success )
block - > resetModified ( ) ;
2010-11-27 00:02:21 +01:00
}
2011-05-18 17:48:27 +02:00
void ServerMap : : loadBlock ( std : : string sectordir , std : : string blockfile , MapSector * sector , bool save_after_load )
2010-11-27 00:02:21 +01:00
{
DSTACK ( __FUNCTION_NAME ) ;
2010-11-29 16:55:07 +01:00
2011-10-16 15:16:47 +02:00
std : : string fullpath = sectordir + DIR_DELIM + blockfile ;
2010-11-29 16:55:07 +01:00
try {
2011-02-16 21:53:35 +01:00
std : : ifstream is ( fullpath . c_str ( ) , std : : ios_base : : binary ) ;
if ( is . good ( ) = = false )
throw FileNotGoodException ( " Cannot open block file " ) ;
2012-12-22 06:34:35 +01:00
2011-02-16 21:53:35 +01:00
v3s16 p3d = getBlockPos ( sectordir , blockfile ) ;
v2s16 p2d ( p3d . X , p3d . Z ) ;
2012-12-22 06:34:35 +01:00
2011-02-16 21:53:35 +01:00
assert ( sector - > getPos ( ) = = p2d ) ;
2012-12-22 06:34:35 +01:00
2011-02-16 21:53:35 +01:00
u8 version = SER_FMT_VER_INVALID ;
is . read ( ( char * ) & version , 1 ) ;
2010-11-27 00:02:21 +01:00
2011-02-16 21:53:35 +01:00
if ( is . fail ( ) )
throw SerializationError ( " ServerMap::loadBlock(): Failed "
" to read MapBlock version " ) ;
2010-11-27 00:02:21 +01:00
2011-02-16 21:53:35 +01:00
/*u32 block_size = MapBlock::serializedLength(version);
SharedBuffer < u8 > data ( block_size ) ;
is . read ( ( char * ) * data , block_size ) ; */
2010-11-27 00:02:21 +01:00
2011-02-16 21:53:35 +01:00
// This will always return a sector because we're the server
//MapSector *sector = emergeSector(p2d);
2010-11-27 00:02:21 +01:00
2011-02-16 21:53:35 +01:00
MapBlock * block = NULL ;
bool created_new = false ;
2011-06-25 23:03:58 +02:00
block = sector - > getBlockNoCreateNoEx ( p3d . Y ) ;
if ( block = = NULL )
2011-02-16 21:53:35 +01:00
{
block = sector - > createBlankBlockNoInsert ( p3d . Y ) ;
created_new = true ;
}
2012-12-22 06:34:35 +01:00
2011-05-22 16:00:09 +02:00
// Read basic data
2012-01-21 00:11:44 +01:00
block - > deSerialize ( is , version , true ) ;
2010-11-27 00:02:21 +01:00
2011-05-22 16:00:09 +02:00
// If it's a new block, insert it to the map
2011-02-16 21:53:35 +01:00
if ( created_new )
sector - > insertBlock ( block ) ;
2012-12-22 06:34:35 +01:00
2011-02-16 21:53:35 +01:00
/*
2011-05-22 16:00:09 +02:00
Save blocks loaded in old format in new format
2011-02-16 21:53:35 +01:00
*/
2011-05-18 17:48:27 +02:00
if ( version < SER_FMT_VER_HIGHEST | | save_after_load )
2011-02-16 21:53:35 +01:00
{
saveBlock ( block ) ;
2012-12-22 06:34:35 +01:00
2011-09-06 19:28:45 +02:00
// Should be in database now, so delete the old file
fs : : RecursiveDelete ( fullpath ) ;
2011-02-16 21:53:35 +01:00
}
2012-12-22 06:34:35 +01:00
2011-02-16 21:53:35 +01:00
// We just loaded it from the disk, so it's up-to-date.
2011-06-25 03:25:14 +02:00
block - > resetModified ( ) ;
2010-11-29 16:55:07 +01:00
}
catch ( SerializationError & e )
{
2011-10-16 13:57:53 +02:00
infostream < < " WARNING: Invalid block data on disk "
2011-06-18 14:16:35 +02:00
< < " fullpath= " < < fullpath
< < " (SerializationError). "
< < " what()= " < < e . what ( )
2010-11-29 16:55:07 +01:00
< < std : : endl ;
2011-06-04 23:10:06 +02:00
//" Ignoring. A new one will be generated.
assert ( 0 ) ;
2011-04-04 23:24:47 +02:00
// TODO: Backup file; name is in fullpath.
2010-11-29 16:55:07 +01:00
}
2010-11-27 00:02:21 +01:00
}
2011-09-03 01:07:14 +02:00
void ServerMap : : loadBlock ( std : : string * blob , v3s16 p3d , MapSector * sector , bool save_after_load )
{
DSTACK ( __FUNCTION_NAME ) ;
try {
std : : istringstream is ( * blob , std : : ios_base : : binary ) ;
2012-12-22 06:34:35 +01:00
2011-09-03 01:07:14 +02:00
u8 version = SER_FMT_VER_INVALID ;
is . read ( ( char * ) & version , 1 ) ;
if ( is . fail ( ) )
throw SerializationError ( " ServerMap::loadBlock(): Failed "
" to read MapBlock version " ) ;
/*u32 block_size = MapBlock::serializedLength(version);
SharedBuffer < u8 > data ( block_size ) ;
is . read ( ( char * ) * data , block_size ) ; */
// This will always return a sector because we're the server
//MapSector *sector = emergeSector(p2d);
MapBlock * block = NULL ;
bool created_new = false ;
block = sector - > getBlockNoCreateNoEx ( p3d . Y ) ;
if ( block = = NULL )
{
block = sector - > createBlankBlockNoInsert ( p3d . Y ) ;
created_new = true ;
}
2012-12-22 06:34:35 +01:00
2011-09-03 01:07:14 +02:00
// Read basic data
2012-01-21 00:11:44 +01:00
block - > deSerialize ( is , version , true ) ;
2012-12-22 06:34:35 +01:00
2011-09-03 01:07:14 +02:00
// If it's a new block, insert it to the map
if ( created_new )
sector - > insertBlock ( block ) ;
2012-12-22 06:34:35 +01:00
2011-09-03 01:07:14 +02:00
/*
Save blocks loaded in old format in new format
*/
2011-11-16 12:03:28 +01:00
//if(version < SER_FMT_VER_HIGHEST || save_after_load)
// Only save if asked to; no need to update version
if ( save_after_load )
2011-09-03 01:07:14 +02:00
saveBlock ( block ) ;
2012-12-22 06:34:35 +01:00
2011-09-03 01:07:14 +02:00
// We just loaded it from, so it's up-to-date.
block - > resetModified ( ) ;
}
catch ( SerializationError & e )
{
2012-06-04 21:34:22 +02:00
errorstream < < " Invalid block data in database "
< < " ( " < < p3d . X < < " , " < < p3d . Y < < " , " < < p3d . Z < < " ) "
< < " (SerializationError): " < < e . what ( ) < < std : : endl ;
2012-12-22 06:34:35 +01:00
2012-06-04 21:34:22 +02:00
// TODO: Block should be marked as invalid in memory so that it is
// not touched but the game can run
2011-09-03 01:07:14 +02:00
2012-06-04 21:34:22 +02:00
if ( g_settings - > getBool ( " ignore_world_load_errors " ) ) {
errorstream < < " Ignoring block load error. Duck and cover! "
< < " (ignore_world_load_errors) " < < std : : endl ;
} else {
throw SerializationError ( " Invalid block data in database " ) ;
//assert(0);
}
2011-09-03 01:07:14 +02:00
}
}
2011-06-25 03:25:14 +02:00
MapBlock * ServerMap : : loadBlock ( v3s16 blockpos )
{
DSTACK ( __FUNCTION_NAME ) ;
v2s16 p2d ( blockpos . X , blockpos . Z ) ;
2011-09-03 01:07:14 +02:00
if ( ! loadFromFolders ( ) ) {
verifyDatabase ( ) ;
2012-12-22 06:34:35 +01:00
2011-09-04 23:01:28 +02:00
if ( sqlite3_bind_int64 ( m_database_read , 1 , getBlockAsInteger ( blockpos ) ) ! = SQLITE_OK )
2011-10-16 13:57:53 +02:00
infostream < < " WARNING: Could not bind block position for load: "
2011-09-03 01:07:14 +02:00
< < sqlite3_errmsg ( m_database ) < < std : : endl ;
if ( sqlite3_step ( m_database_read ) = = SQLITE_ROW ) {
/*
Make sure sector is loaded
*/
MapSector * sector = createSector ( p2d ) ;
2012-12-22 06:34:35 +01:00
2011-09-03 01:07:14 +02:00
/*
Load block
*/
const char * data = ( const char * ) sqlite3_column_blob ( m_database_read , 0 ) ;
size_t len = sqlite3_column_bytes ( m_database_read , 0 ) ;
2012-12-22 06:34:35 +01:00
2011-09-03 01:07:14 +02:00
std : : string datastr ( data , len ) ;
2012-12-22 06:34:35 +01:00
2011-09-03 01:07:14 +02:00
loadBlock ( & datastr , blockpos , sector , false ) ;
sqlite3_step ( m_database_read ) ;
// We should never get more than 1 row, so ok to reset
sqlite3_reset ( m_database_read ) ;
return getBlockNoCreateNoEx ( blockpos ) ;
}
sqlite3_reset ( m_database_read ) ;
2012-12-22 06:34:35 +01:00
2011-09-03 01:07:14 +02:00
// Not found in database, try the files
}
2011-06-25 03:25:14 +02:00
// The directory layout we're going to load from.
// 1 - original sectors/xxxxzzzz/
// 2 - new sectors2/xxx/zzz/
// If we load from anything but the latest structure, we will
// immediately save to the new one, and remove the old.
int loadlayout = 1 ;
std : : string sectordir1 = getSectorDir ( p2d , 1 ) ;
std : : string sectordir ;
if ( fs : : PathExists ( sectordir1 ) )
{
sectordir = sectordir1 ;
}
else
{
loadlayout = 2 ;
sectordir = getSectorDir ( p2d , 2 ) ;
}
2012-12-22 06:34:35 +01:00
2011-06-25 03:25:14 +02:00
/*
Make sure sector is loaded
*/
MapSector * sector = getSectorNoGenerateNoEx ( p2d ) ;
if ( sector = = NULL )
{
try {
sector = loadSectorMeta ( sectordir , loadlayout ! = 2 ) ;
}
catch ( InvalidFilenameException & e )
{
2012-06-02 10:01:28 +02:00
return NULL ;
2011-06-25 03:25:14 +02:00
}
catch ( FileNotGoodException & e )
{
2012-06-02 10:01:28 +02:00
return NULL ;
2011-06-25 03:25:14 +02:00
}
catch ( std : : exception & e )
{
2012-06-02 10:01:28 +02:00
return NULL ;
2011-06-25 03:25:14 +02:00
}
}
2012-12-22 06:34:35 +01:00
2011-06-25 03:25:14 +02:00
/*
Make sure file exists
*/
std : : string blockfilename = getBlockFilename ( blockpos ) ;
2011-10-16 15:16:47 +02:00
if ( fs : : PathExists ( sectordir + DIR_DELIM + blockfilename ) = = false )
2011-06-25 03:25:14 +02:00
return NULL ;
/*
2011-09-03 01:07:14 +02:00
Load block and save it to the database
2011-06-25 03:25:14 +02:00
*/
2011-09-03 01:07:14 +02:00
loadBlock ( sectordir , blockfilename , sector , true ) ;
2011-06-25 03:25:14 +02:00
return getBlockNoCreateNoEx ( blockpos ) ;
}
2010-11-27 00:02:21 +01:00
void ServerMap : : PrintInfo ( std : : ostream & out )
{
out < < " ServerMap: " ;
}
2010-12-11 17:11:03 +01:00
/*
MapVoxelManipulator
*/
MapVoxelManipulator : : MapVoxelManipulator ( Map * map )
{
m_map = map ;
}
MapVoxelManipulator : : ~ MapVoxelManipulator ( )
{
2011-10-16 13:57:53 +02:00
/*infostream<<"MapVoxelManipulator: blocks: "<<m_loaded_blocks.size()
2010-12-13 20:32:35 +01:00
< < std : : endl ; */
2010-12-11 17:11:03 +01:00
}
2010-12-12 13:33:13 +01:00
void MapVoxelManipulator : : emerge ( VoxelArea a , s32 caller_id )
2010-12-11 17:11:03 +01:00
{
2010-12-21 17:08:24 +01:00
TimeTaker timer1 ( " emerge " , & emerge_time ) ;
2010-12-11 17:11:03 +01:00
// Units of these are MapBlocks
v3s16 p_min = getNodeBlockPos ( a . MinEdge ) ;
v3s16 p_max = getNodeBlockPos ( a . MaxEdge ) ;
VoxelArea block_area_nodes
( p_min * MAP_BLOCKSIZE , ( p_max + 1 ) * MAP_BLOCKSIZE - v3s16 ( 1 , 1 , 1 ) ) ;
addArea ( block_area_nodes ) ;
for ( s32 z = p_min . Z ; z < = p_max . Z ; z + + )
for ( s32 y = p_min . Y ; y < = p_max . Y ; y + + )
for ( s32 x = p_min . X ; x < = p_max . X ; x + + )
{
2013-02-17 07:47:49 +01:00
u8 flags = 0 ;
MapBlock * block ;
2010-12-11 17:11:03 +01:00
v3s16 p ( x , y , z ) ;
2013-02-17 07:47:49 +01:00
core : : map < v3s16 , u8 > : : Node * n ;
2010-12-11 17:11:03 +01:00
n = m_loaded_blocks . find ( p ) ;
if ( n ! = NULL )
continue ;
2012-12-22 06:34:35 +01:00
2010-12-11 17:11:03 +01:00
bool block_data_inexistent = false ;
try
{
2010-12-21 17:08:24 +01:00
TimeTaker timer1 ( " emerge load " , & emerge_load_time ) ;
2010-12-11 17:11:03 +01:00
2011-10-16 13:57:53 +02:00
/*infostream<<"Loading block (caller_id="<<caller_id<<")"
2010-12-12 13:33:13 +01:00
< < " ( " < < p . X < < " , " < < p . Y < < " , " < < p . Z < < " ) "
< < " wanted area: " ;
2011-10-16 13:57:53 +02:00
a . print ( infostream ) ;
infostream < < std : : endl ; */
2012-12-22 06:34:35 +01:00
2013-02-17 07:47:49 +01:00
block = m_map - > getBlockNoCreate ( p ) ;
2010-12-11 17:11:03 +01:00
if ( block - > isDummy ( ) )
block_data_inexistent = true ;
else
block - > copyTo ( * this ) ;
}
catch ( InvalidPositionException & e )
{
block_data_inexistent = true ;
}
if ( block_data_inexistent )
{
2013-02-17 07:47:49 +01:00
flags | = VMANIP_BLOCK_DATA_INEXIST ;
2010-12-11 17:11:03 +01:00
VoxelArea a ( p * MAP_BLOCKSIZE , ( p + 1 ) * MAP_BLOCKSIZE - v3s16 ( 1 , 1 , 1 ) ) ;
// Fill with VOXELFLAG_INEXISTENT
for ( s32 z = a . MinEdge . Z ; z < = a . MaxEdge . Z ; z + + )
for ( s32 y = a . MinEdge . Y ; y < = a . MaxEdge . Y ; y + + )
{
s32 i = m_area . index ( a . MinEdge . X , y , z ) ;
memset ( & m_flags [ i ] , VOXELFLAG_INEXISTENT , MAP_BLOCKSIZE ) ;
}
}
2013-02-22 04:26:21 +01:00
/*else if (block->getNode(0, 0, 0).getContent() == CONTENT_IGNORE)
2013-02-17 07:47:49 +01:00
{
// Mark that block was loaded as blank
flags | = VMANIP_BLOCK_CONTAINS_CIGNORE ;
2013-02-22 04:26:21 +01:00
} */
2010-12-11 17:11:03 +01:00
2013-02-17 07:47:49 +01:00
m_loaded_blocks . insert ( p , flags ) ;
2010-12-11 17:11:03 +01:00
}
2011-10-16 13:57:53 +02:00
//infostream<<"emerge done"<<std::endl;
2010-12-11 17:11:03 +01:00
}
/*
2011-01-25 23:41:06 +01:00
SUGG : Add an option to only update eg . water and air nodes .
2010-12-11 17:11:03 +01:00
This will make it interfere less with important stuff if
run on background .
*/
void MapVoxelManipulator : : blitBack
( core : : map < v3s16 , MapBlock * > & modified_blocks )
{
2010-12-12 13:33:13 +01:00
if ( m_area . getExtent ( ) = = v3s16 ( 0 , 0 , 0 ) )
return ;
2012-12-22 06:34:35 +01:00
2010-12-21 17:08:24 +01:00
//TimeTaker timer1("blitBack");
2011-01-24 15:36:58 +01:00
2011-10-16 13:57:53 +02:00
/*infostream<<"blitBack(): m_loaded_blocks.size()="
2011-01-24 15:36:58 +01:00
< < m_loaded_blocks . size ( ) < < std : : endl ; */
2012-12-22 06:34:35 +01:00
2010-12-11 17:11:03 +01:00
/*
Initialize block cache
*/
v3s16 blockpos_last ;
MapBlock * block = NULL ;
bool block_checked_in_modified = false ;
for ( s32 z = m_area . MinEdge . Z ; z < = m_area . MaxEdge . Z ; z + + )
for ( s32 y = m_area . MinEdge . Y ; y < = m_area . MaxEdge . Y ; y + + )
for ( s32 x = m_area . MinEdge . X ; x < = m_area . MaxEdge . X ; x + + )
{
v3s16 p ( x , y , z ) ;
u8 f = m_flags [ m_area . index ( p ) ] ;
if ( f & ( VOXELFLAG_NOT_LOADED | VOXELFLAG_INEXISTENT ) )
continue ;
MapNode & n = m_data [ m_area . index ( p ) ] ;
2012-12-22 06:34:35 +01:00
2010-12-11 17:11:03 +01:00
v3s16 blockpos = getNodeBlockPos ( p ) ;
2012-12-22 06:34:35 +01:00
2010-12-11 17:11:03 +01:00
try
{
// Get block
if ( block = = NULL | | blockpos ! = blockpos_last ) {
block = m_map - > getBlockNoCreate ( blockpos ) ;
blockpos_last = blockpos ;
block_checked_in_modified = false ;
}
2012-12-22 06:34:35 +01:00
2010-12-11 17:11:03 +01:00
// Calculate relative position in block
v3s16 relpos = p - blockpos * MAP_BLOCKSIZE ;
// Don't continue if nothing has changed here
if ( block - > getNode ( relpos ) = = n )
continue ;
//m_map->setNode(m_area.MinEdge + p, n);
block - > setNode ( relpos , n ) ;
2012-12-22 06:34:35 +01:00
2010-12-11 17:11:03 +01:00
/*
Make sure block is in modified_blocks
*/
if ( block_checked_in_modified = = false )
{
modified_blocks [ blockpos ] = block ;
block_checked_in_modified = true ;
}
}
catch ( InvalidPositionException & e )
{
}
}
}
2011-01-24 15:36:58 +01:00
ManualMapVoxelManipulator : : ManualMapVoxelManipulator ( Map * map ) :
2011-04-11 10:36:13 +02:00
MapVoxelManipulator ( map ) ,
m_create_area ( false )
2011-01-24 15:36:58 +01:00
{
}
ManualMapVoxelManipulator : : ~ ManualMapVoxelManipulator ( )
{
}
void ManualMapVoxelManipulator : : emerge ( VoxelArea a , s32 caller_id )
{
2011-02-01 02:06:02 +01:00
// Just create the area so that it can be pointed to
2011-01-24 15:36:58 +01:00
VoxelManipulator : : emerge ( a , caller_id ) ;
}
void ManualMapVoxelManipulator : : initialEmerge (
v3s16 blockpos_min , v3s16 blockpos_max )
{
2011-02-01 15:17:55 +01:00
TimeTaker timer1 ( " initialEmerge " , & emerge_time ) ;
2011-01-24 15:36:58 +01:00
// Units of these are MapBlocks
v3s16 p_min = blockpos_min ;
v3s16 p_max = blockpos_max ;
VoxelArea block_area_nodes
( p_min * MAP_BLOCKSIZE , ( p_max + 1 ) * MAP_BLOCKSIZE - v3s16 ( 1 , 1 , 1 ) ) ;
2012-12-22 06:34:35 +01:00
2011-02-01 15:17:55 +01:00
u32 size_MB = block_area_nodes . getVolume ( ) * 4 / 1000000 ;
if ( size_MB > = 1 )
{
2011-10-16 13:57:53 +02:00
infostream < < " initialEmerge: area: " ;
block_area_nodes . print ( infostream ) ;
infostream < < " ( " < < size_MB < < " MB) " ;
infostream < < std : : endl ;
2011-02-01 15:17:55 +01:00
}
2011-01-24 15:36:58 +01:00
addArea ( block_area_nodes ) ;
for ( s32 z = p_min . Z ; z < = p_max . Z ; z + + )
for ( s32 y = p_min . Y ; y < = p_max . Y ; y + + )
for ( s32 x = p_min . X ; x < = p_max . X ; x + + )
{
2013-02-17 07:47:49 +01:00
u8 flags = 0 ;
MapBlock * block ;
2011-01-24 15:36:58 +01:00
v3s16 p ( x , y , z ) ;
2013-02-17 07:47:49 +01:00
core : : map < v3s16 , u8 > : : Node * n ;
2011-01-24 15:36:58 +01:00
n = m_loaded_blocks . find ( p ) ;
if ( n ! = NULL )
continue ;
2012-12-22 06:34:35 +01:00
2011-01-24 15:36:58 +01:00
bool block_data_inexistent = false ;
try
{
TimeTaker timer1 ( " emerge load " , & emerge_load_time ) ;
2013-02-17 07:47:49 +01:00
block = m_map - > getBlockNoCreate ( p ) ;
2011-01-24 15:36:58 +01:00
if ( block - > isDummy ( ) )
block_data_inexistent = true ;
else
block - > copyTo ( * this ) ;
}
catch ( InvalidPositionException & e )
{
block_data_inexistent = true ;
}
if ( block_data_inexistent )
{
2013-02-17 07:47:49 +01:00
flags | = VMANIP_BLOCK_DATA_INEXIST ;
2011-02-01 02:06:02 +01:00
/*
Mark area inexistent
*/
2011-01-24 15:36:58 +01:00
VoxelArea a ( p * MAP_BLOCKSIZE , ( p + 1 ) * MAP_BLOCKSIZE - v3s16 ( 1 , 1 , 1 ) ) ;
// Fill with VOXELFLAG_INEXISTENT
for ( s32 z = a . MinEdge . Z ; z < = a . MaxEdge . Z ; z + + )
for ( s32 y = a . MinEdge . Y ; y < = a . MaxEdge . Y ; y + + )
{
s32 i = m_area . index ( a . MinEdge . X , y , z ) ;
memset ( & m_flags [ i ] , VOXELFLAG_INEXISTENT , MAP_BLOCKSIZE ) ;
}
}
2013-02-22 04:26:21 +01:00
/*else if (block->getNode(0, 0, 0).getContent() == CONTENT_IGNORE)
2013-02-17 07:47:49 +01:00
{
// Mark that block was loaded as blank
flags | = VMANIP_BLOCK_CONTAINS_CIGNORE ;
2013-02-22 04:26:21 +01:00
} */
2011-01-24 15:36:58 +01:00
2013-02-17 07:47:49 +01:00
m_loaded_blocks . insert ( p , flags ) ;
2011-02-01 02:06:02 +01:00
}
}
void ManualMapVoxelManipulator : : blitBackAll (
core : : map < v3s16 , MapBlock * > * modified_blocks )
{
if ( m_area . getExtent ( ) = = v3s16 ( 0 , 0 , 0 ) )
return ;
2012-12-22 06:34:35 +01:00
2011-02-01 02:06:02 +01:00
/*
Copy data of all blocks
*/
2013-02-17 07:47:49 +01:00
for ( core : : map < v3s16 , u8 > : : Iterator
2011-02-01 02:06:02 +01:00
i = m_loaded_blocks . getIterator ( ) ;
i . atEnd ( ) = = false ; i + + )
{
2011-07-21 16:00:08 +02:00
v3s16 p = i . getNode ( ) - > getKey ( ) ;
2013-02-17 07:47:49 +01:00
u8 flags = i . getNode ( ) - > getValue ( ) ;
bool existed = ! ( flags & VMANIP_BLOCK_DATA_INEXIST ) ;
2011-02-01 02:06:02 +01:00
if ( existed = = false )
2011-07-21 16:00:08 +02:00
{
// The Great Bug was found using this
2011-10-16 13:57:53 +02:00
/*infostream<<"ManualMapVoxelManipulator::blitBackAll: "
2011-07-21 16:00:08 +02:00
< < " Inexistent ( " < < p . X < < " , " < < p . Y < < " , " < < p . Z < < " ) "
< < std : : endl ; */
2011-02-01 02:06:02 +01:00
continue ;
2011-07-21 16:00:08 +02:00
}
2013-02-17 07:47:49 +01:00
2011-02-01 02:06:02 +01:00
MapBlock * block = m_map - > getBlockNoCreateNoEx ( p ) ;
if ( block = = NULL )
{
2011-10-16 13:57:53 +02:00
infostream < < " WARNING: " < < __FUNCTION_NAME
2011-02-01 02:06:02 +01:00
< < " : got NULL block "
< < " ( " < < p . X < < " , " < < p . Y < < " , " < < p . Z < < " ) "
< < std : : endl ;
continue ;
}
2013-02-22 04:26:21 +01:00
block - > copyFrom ( * this ) ;
if ( modified_blocks )
modified_blocks - > insert ( p , block ) ;
2011-01-24 15:36:58 +01:00
}
}
2010-12-11 17:11:03 +01:00
//END