map generation framework under development... not quite operational at this point.

This commit is contained in:
Perttu Ahola 2011-01-30 01:44:54 +02:00
parent 8788fffec0
commit be851871cd
13 changed files with 965 additions and 372 deletions

@ -76,21 +76,17 @@ with this program; if not, write to the Free Software Foundation, Inc.,
// is very low
#define BLOCK_SEND_DISABLE_LIMITS_MAX_D 1
// The fps limiter will leave this much free time
//#define FREETIME_RATIO 0.15
//#define FREETIME_RATIO 0.0
#define FREETIME_RATIO 0.05
#define PLAYER_INVENTORY_SIZE (8*4)
#define SIGN_TEXT_MAX_LENGTH 50
// Whether to catch all std::exceptions.
// Assert will be called on such an event.
#ifdef DEBUG
#define CATCH_UNHANDLED_EXCEPTIONS 0
#else
// In debug mode, leave these for the debugger and don't catch them.
#ifdef NDEBUG
#define CATCH_UNHANDLED_EXCEPTIONS 1
#else
#define CATCH_UNHANDLED_EXCEPTIONS 0
#endif
/*

@ -49,8 +49,8 @@ void set_default_settings()
g_settings.setDefault("max_simultaneous_block_sends_per_client", "1");
//g_settings.setDefault("max_simultaneous_block_sends_per_client", "2");
g_settings.setDefault("max_simultaneous_block_sends_server_total", "4");
g_settings.setDefault("max_block_send_distance", "6");
g_settings.setDefault("max_block_generate_distance", "6");
g_settings.setDefault("max_block_send_distance", "7");
g_settings.setDefault("max_block_generate_distance", "7");
g_settings.setDefault("time_send_interval", "20");
g_settings.setDefault("time_speed", "96");
g_settings.setDefault("server_unload_unused_sectors_timeout", "60");

@ -168,7 +168,7 @@ TODO: Make fetching sector's blocks more efficient when rendering
TODO: Flowing water animation
FIXME: The new texture stuff is slow on wine
FIXME(FIXED): The new texture stuff is slow on wine
- A basic grassy ground block takes 20-40ms
- A bit more complicated block can take 270ms
- On linux, a similar one doesn't take long at all (14ms)
@ -182,6 +182,15 @@ FIXME: The new texture stuff is slow on wine
is fast to compare, which refers to a cached string, or
* Make TextureSpec for using instead of strings
FIXME(FIXED): A lock condition is possible:
1) MapBlock::updateMesh() is called from client asynchronously:
- AsyncProcessData() -> Map::updateMeshes()
2) Asynchronous locks m_temp_mods_mutex
3) MapBlock::updateMesh() is called from client synchronously:
- Client::step() -> Environment::step()
4) Synchronous starts waiting for m_temp_mods_mutex
5) Asynchronous calls getTexture, which starts waiting for main thread
Configuration:
--------------
@ -255,6 +264,20 @@ Map:
NOTE: There are some lighting-related todos and fixmes in
ServerMap::emergeBlock. And there always will be. 8)
TODO: Mineral and ground material properties
- This way mineral ground toughness can be calculated with just
some formula, as well as tool strengths
TODO: Change AttributeList to split the area into smaller sections so
that searching won't be as heavy.
TODO: Remove HMParams
TODO: Flowing water to actually contain flow direction information
TODO: Remove duplicate lighting implementation from Map (leave
VoxelManipulator, which is faster)
FEATURE: Map generator version 2
- Create surface areas based on central points; a given point's
area type is given by the nearest central point
@ -269,6 +292,11 @@ FEATURE: Map generator version 2
FEATURE: The map could be generated procedually:
- This would need the map to be generated in larger pieces
- How large? How do they connect to each other?
- It has to be split vertically also
- Lighting would not have to be necessarily calculated until
the blocks are actually needed - it would be quite fast
- Something like 64*64*16 MapBlocks?
- TODO: Separate lighting and block generation
* Make the stone level with a heightmap
* Carve out stuff in the stone
* Dump dirt all around, and simulate it falling off steep
@ -283,20 +311,13 @@ FEATURE: The map could be generated procedually:
parameter field is free for this.
- Simulate rock falling from cliffs when water has removed
enough solid rock from the bottom
TODO: Mineral and ground material properties
- This way mineral ground toughness can be calculated with just
some formula, as well as tool strengths
TODO: Change AttributeList to split the area into smaller sections so
that searching won't be as heavy.
TODO: Remove HMParams
TODO: Flowing water to actually contain flow direction information
TODO: Remove duplicate lighting implementation from Map (leave
VoxelManipulator, which is faster)
TODO: Lazy lighting updates:
- Set updateLighting to ignore MapBlocks with expired lighting,
except the blocks specified to it
- When a MapBlock is generated, lighting expires in all blocks
touching it (26 blocks + self)
- When a lighting-wise valid MapBlock is needed and lighting of it
has expired, what to do?
Doing now:
----------
@ -1522,6 +1543,16 @@ int main(int argc, char *argv[])
srand(time(0));
mysrand(time(0));
/*
Pre-initialize some stuff with a dummy irrlicht wrapper.
These are needed for unit tests at least.
*/
IIrrlichtWrapper irrlicht_dummy;
init_mapnode(&irrlicht_dummy);
/*
Run unit tests
*/
@ -1684,7 +1715,7 @@ int main(int argc, char *argv[])
skin->setColor(gui::EGDC_3D_SHADOW, video::SColor(255,0,0,0));
/*
Preload some textures
Preload some textures and stuff
*/
init_content_inventory_texture_paths();
@ -2131,20 +2162,6 @@ int main(int argc, char *argv[])
dtime_jitter1_max_fraction
= dtime_jitter1_max_sample / (dtime_avg1+0.001);
jitter1_max = 0.0;
/*
Control freetime ratio
*/
/*if(dtime_jitter1_max_fraction > DTIME_JITTER_MAX_FRACTION)
{
if(g_freetime_ratio < FREETIME_RATIO_MAX)
g_freetime_ratio += 0.01;
}
else
{
if(g_freetime_ratio > FREETIME_RATIO_MIN)
g_freetime_ratio -= 0.01;
}*/
}
}

@ -67,10 +67,8 @@ Map::~Map()
}
}
MapSector * Map::getSectorNoGenerate(v2s16 p)
MapSector * Map::getSectorNoGenerateNoExNoLock(v2s16 p)
{
JMutexAutoLock lock(m_sector_mutex);
if(m_sector_cache != NULL && p == m_sector_cache_p){
MapSector * sector = m_sector_cache;
// Reset inactivity timer
@ -79,11 +77,9 @@ MapSector * Map::getSectorNoGenerate(v2s16 p)
}
core::map<v2s16, MapSector*>::Node *n = m_sectors.find(p);
// If sector doesn't exist, throw an exception
if(n == NULL)
{
throw InvalidPositionException();
}
return NULL;
MapSector *sector = n->getValue();
@ -91,13 +87,27 @@ MapSector * Map::getSectorNoGenerate(v2s16 p)
m_sector_cache_p = p;
m_sector_cache = sector;
//MapSector * ref(sector);
// Reset inactivity timer
sector->usage_timer = 0.0;
return sector;
}
MapSector * Map::getSectorNoGenerateNoEx(v2s16 p)
{
JMutexAutoLock lock(m_sector_mutex);
return getSectorNoGenerateNoExNoLock(p);
}
MapSector * Map::getSectorNoGenerate(v2s16 p)
{
MapSector *sector = getSectorNoGenerateNoEx(p);
if(sector == NULL)
throw InvalidPositionException();
return sector;
}
MapBlock * Map::getBlockNoCreate(v3s16 p3d)
{
v2s16 p2d(p3d.X, p3d.Z);
@ -630,6 +640,8 @@ void Map::updateLighting(enum LightBank bank,
// For debugging
//bool debug=true;
//u32 count_was = modified_blocks.size();
core::map<v3s16, MapBlock*> blocks_to_update;
core::map<v3s16, bool> light_sources;
@ -650,6 +662,8 @@ void Map::updateLighting(enum LightBank bank,
v3s16 pos = block->getPos();
modified_blocks.insert(pos, block);
blocks_to_update.insert(pos, block);
/*
Clear all light from block
*/
@ -699,10 +713,12 @@ void Map::updateLighting(enum LightBank bank,
}
else if(bank == LIGHTBANK_NIGHT)
{
// For night lighting, sunlight is not propagated
break;
}
else
{
// Invalid lighting bank
assert(0);
}
@ -710,7 +726,7 @@ void Map::updateLighting(enum LightBank bank,
<<pos.X<<","<<pos.Y<<","<<pos.Z<<") not valid"
<<std::endl;*/
// Else get the block below and loop to it
// Bottom sunlight is not valid; get the block and loop to it
pos.Y--;
try{
@ -737,13 +753,6 @@ void Map::updateLighting(enum LightBank bank,
dstream<<"unspreadLight modified "<<diff<<std::endl;
}
// TODO: Spread light from propagated sunlight?
// Yes, add it to light_sources... somehow.
// It has to be added at somewhere above, in the loop.
// TODO
// NOTE: This actually works fine without doing so
// - Find out why it works
{
TimeTaker timer("spreadLight");
spreadLight(bank, light_sources, modified_blocks);
@ -759,17 +768,44 @@ void Map::updateLighting(enum LightBank bank,
{
//MapVoxelManipulator vmanip(this);
ManualMapVoxelManipulator vmanip(this);
// Make a manual voxel manipulator and load all the blocks
// that touch the requested blocks
ManualMapVoxelManipulator vmanip(this);
core::map<v3s16, MapBlock*>::Iterator i;
i = a_blocks.getIterator();
i = blocks_to_update.getIterator();
for(; i.atEnd() == false; i++)
{
MapBlock *block = i.getNode()->getValue();
v3s16 p = block->getPos();
// Add all surrounding blocks
vmanip.initialEmerge(p - v3s16(1,1,1), p + v3s16(1,1,1));
/*
Add all surrounding blocks that have up-to-date lighting
NOTE: This doesn't quite do the job (not everything
appropriate is lighted)
*/
/*for(s16 z=-1; z<=1; z++)
for(s16 y=-1; y<=1; y++)
for(s16 x=-1; x<=1; x++)
{
v3s16 p(x,y,z);
MapBlock *block = getBlockNoCreateNoEx(p);
if(block == NULL)
continue;
if(block->isDummy())
continue;
if(block->getLightingExpired())
continue;
vmanip.initialEmerge(p, p);
}*/
// Lighting of block will be updated completely
block->setLightingExpired(false);
}
{
//TimeTaker timer("unSpreadLight");
vmanip.unspreadLight(bank, unlight_from, light_sources);
@ -1407,6 +1443,8 @@ void Map::transformLiquids(core::map<v3s16, MapBlock*> & modified_blocks)
u32 loopcount = 0;
u32 initial_size = m_transforming_liquid.size();
//dstream<<"transformLiquids(): initial_size="<<initial_size<<std::endl;
while(m_transforming_liquid.size() != 0)
{
/*
@ -1682,6 +1720,12 @@ ServerMap::ServerMap(std::string savedir, HMParams hmp, MapParams mp):
Map(dout_server),
m_heightmap(NULL)
{
//m_chunksize = 64;
//m_chunksize = 16;
//m_chunksize = 8;
m_chunksize = 2;
/*
Experimental and debug stuff
*/
@ -1862,7 +1906,7 @@ ServerMap::ServerMap(std::string savedir, HMParams hmp, MapParams mp):
list_randmax->addPoint(v3s16(0,0,0), Attribute(10));
list_randfactor->addPoint(v3s16(0,0,0), Attribute(0.65));*/
}
/*
Try to load map; if not found, create a new one.
*/
@ -1917,18 +1961,6 @@ ServerMap::ServerMap(std::string savedir, HMParams hmp, MapParams mp):
dstream<<DTIME<<"Initializing new map."<<std::endl;
// Create master heightmap
/*ValueGenerator *maxgen =
ValueGenerator::deSerialize(hmp.randmax);
ValueGenerator *factorgen =
ValueGenerator::deSerialize(hmp.randfactor);
ValueGenerator *basegen =
ValueGenerator::deSerialize(hmp.base);
m_heightmap = new UnlimitedHeightmap
(hmp.blocksize, maxgen, factorgen, basegen, &m_padb);*/
/*m_heightmap = new UnlimitedHeightmap
(hmp.blocksize, &m_padb);*/
m_heightmap = new UnlimitedHeightmap
(32, &m_padb);
@ -1966,29 +1998,134 @@ ServerMap::~ServerMap()
if(m_heightmap != NULL)
delete m_heightmap;
/*
Free all MapChunks
*/
core::map<v2s16, MapChunk*>::Iterator i = m_chunks.getIterator();
for(; i.atEnd() == false; i++)
{
MapChunk *chunk = i.getNode()->getValue();
delete chunk;
}
}
MapSector * ServerMap::emergeSector(v2s16 p2d)
MapChunk* ServerMap::generateChunkRaw(v2s16 chunkpos)
{
// Return if chunk already exists
MapChunk *chunk = getChunk(chunkpos);
if(chunk)
return chunk;
/*
Add all sectors
*/
dstream<<"generateChunkRaw(): "
<<"("<<chunkpos.X<<","<<chunkpos.Y<<")"
<<std::endl;
TimeTaker timer("generateChunkRaw()");
v2s16 sectorpos_base = chunk_to_sector(chunkpos);
core::map<v3s16, MapBlock*> changed_blocks;
core::map<v3s16, MapBlock*> lighting_invalidated_blocks;
u32 generated_block_count = 0;
for(s16 y=0; y<m_chunksize; y++)
{
/*dstream<<"Generating sectors "
<<"("<<sectorpos_base.X<<"..."
<<(sectorpos_base.X+m_chunksize-1)
<<", "<<y<<")"
<<std::endl;*/
// With caves_amount attribute fetch: ~90ms (379ms peaks)
// Without: ~38ms (396ms peaks)
//TimeTaker timer("Chunk sector row");
for(s16 x=0; x<m_chunksize; x++)
{
v2s16 sectorpos = sectorpos_base + v2s16(x,y);
/*dstream<<"Generating sector "
<<"("<<sectorpos.X<<","<<sectorpos.Y<<")"
<<std::endl;*/
// Generate sector
ServerMapSector *sector = generateSector(sectorpos);
/*
Generate main blocks of sector
*/
s16 d = 8;
for(s16 y2=-d/2; y2<d/2; y2++)
{
v3s16 p(x,y2,y);
// Check that the block doesn't exist already
if(sector->getBlockNoCreateNoEx(y2))
continue;
generateBlock(p, NULL, sector, changed_blocks,
lighting_invalidated_blocks);
generated_block_count++;
}
}
}
dstream<<"generateChunkRaw generated "<<generated_block_count
<<" blocks"<<std::endl;
{
TimeTaker timer2("generateChunkRaw() lighting");
// Update lighting
core::map<v3s16, MapBlock*> lighting_modified_blocks;
updateLighting(lighting_invalidated_blocks, lighting_modified_blocks);
}
// Add chunk meta information
chunk = new MapChunk();
m_chunks.insert(chunkpos, chunk);
return chunk;
}
MapChunk* ServerMap::generateChunk(v2s16 chunkpos)
{
/*
Generate chunk and neighbors
*/
for(s16 x=-1; x<=1; x++)
for(s16 y=-1; y<=1; y++)
{
generateChunkRaw(chunkpos + v2s16(x,y));
}
/*
Get chunk
*/
MapChunk *chunk = getChunk(chunkpos);
assert(chunk);
// Set non-volatile
chunk->setIsVolatile(false);
// Return it
return chunk;
}
ServerMapSector * ServerMap::generateSector(v2s16 p2d)
{
DSTACK("%s: p2d=(%d,%d)",
__FUNCTION_NAME,
p2d.X, p2d.Y);
// Check that it doesn't exist already
try{
return getSectorNoGenerate(p2d);
}
catch(InvalidPositionException &e)
{
}
/*
Try to load the sector from disk.
*/
if(loadSectorFull(p2d) == true)
{
return getSectorNoGenerate(p2d);
}
// Check that it doesn't exist already
ServerMapSector *sector = (ServerMapSector*)getSectorNoGenerateNoEx(p2d);
if(sector != NULL)
return sector;
/*
If there is no master heightmap, throw.
*/
@ -2016,7 +2153,7 @@ MapSector * ServerMap::emergeSector(v2s16 p2d)
// Heightmap side width
s16 hm_d = MAP_BLOCKSIZE / hm_split;
ServerMapSector *sector = new ServerMapSector(this, p2d, hm_split);
sector = new ServerMapSector(this, p2d, hm_split);
// Sector position on map in nodes
v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
@ -2068,7 +2205,9 @@ MapSector * ServerMap::emergeSector(v2s16 p2d)
Get local attributes
*/
float local_plants_amount = 0.0;
float local_plants_amount = 0.5;
#if 0
{
//dstream<<"emergeSector(): Reading point attribute lists"<<std::endl;
//TimeTaker attrtimer("emergeSector() attribute fetch");
@ -2081,6 +2220,7 @@ MapSector * ServerMap::emergeSector(v2s16 p2d)
local_plants_amount =
palist->getInterpolatedFloat(nodepos2d);
}
#endif
/*
Generate sector heightmap
@ -2201,91 +2341,104 @@ MapSector * ServerMap::emergeSector(v2s16 p2d)
/*
Insert to container
*/
JMutexAutoLock lock(m_sector_mutex);
m_sectors.insert(p2d, sector);
return sector;
}
MapBlock * ServerMap::emergeBlock(
MapSector * ServerMap::emergeSector(v2s16 p2d)
{
DSTACK("%s: p2d=(%d,%d)",
__FUNCTION_NAME,
p2d.X, p2d.Y);
/*
Check if it exists already in memory
*/
MapSector *sector = getSectorNoGenerateNoEx(p2d);
if(sector != NULL)
return sector;
/*
Try to load it from disk
*/
if(loadSectorFull(p2d) == true)
{
MapSector *sector = getSectorNoGenerateNoEx(p2d);
if(sector == NULL)
{
dstream<<"ServerMap::emergeSector(): loadSectorFull didn't make a sector"<<std::endl;
throw InvalidPositionException("");
}
return sector;
}
/*
Check chunk status
*/
v2s16 chunkpos = sector_to_chunk(p2d);
bool chunk_exists = false;
MapChunk *chunk = getChunk(chunkpos);
if(chunk && chunk->getIsVolatile() == false)
chunk_exists = true;
/*
If chunk is not generated, generate chunk
*/
if(chunk_exists == false)
{
// Generate chunk and neighbors
generateChunk(chunkpos);
}
/*
Return sector if it exists now
*/
sector = getSectorNoGenerateNoEx(p2d);
if(sector != NULL)
return sector;
/*
generateChunk should have generated the sector
*/
assert(0);
/*
Generate directly
*/
//return generateSector();
}
MapBlock * ServerMap::generateBlock(
v3s16 p,
bool only_from_disk,
MapBlock *original_dummy,
ServerMapSector *sector,
core::map<v3s16, MapBlock*> &changed_blocks,
core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
)
{
DSTACK("%s: p=(%d,%d,%d), only_from_disk=%d",
DSTACK("%s: p=(%d,%d,%d)",
__FUNCTION_NAME,
p.X, p.Y, p.Z, only_from_disk);
/*dstream<<"ServerMap::emergeBlock(): "
p.X, p.Y, p.Z);
/*dstream<<"generateBlock(): "
<<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
<<", only_from_disk="<<only_from_disk<<std::endl;*/
<<std::endl;*/
MapBlock *block = original_dummy;
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 = (ServerMapSector*)emergeSector(p2d);
assert(sector->getId() == MAPSECTOR_SERVER);
// Try to get a block from the sector
MapBlock *block = NULL;
bool not_on_disk = false;
try{
block = sector->getBlockNoCreate(block_y);
if(block->isDummy() == true)
not_on_disk = true;
else
return block;
}
catch(InvalidPositionException &e)
{
not_on_disk = true;
}
/*
If block was not found on disk and not going to generate a
new one, make sure there is a dummy block in place.
*/
if(not_on_disk && only_from_disk)
{
if(block == NULL)
{
// Create dummy block
block = new MapBlock(this, p, true);
// Add block to sector
sector->insertBlock(block);
}
// Done.
return block;
}
//dstream<<"Not found on disk, generating."<<std::endl;
// 0ms
//TimeTaker("emergeBlock() generate");
/*
Do not generate over-limit
*/
if(blockpos_over_limit(p))
throw InvalidPositionException("emergeBlock(): pos. over limit");
{
dstream<<__FUNCTION_NAME<<": Block position over limit"<<std::endl;
throw InvalidPositionException("generateBlock(): pos. over limit");
}
/*
OK; Not found.
Go on generating the block.
TODO: If a dungeon gets generated so that it's side gets
revealed to the outside air, the lighting should be
recalculated.
*/
/*
If block doesn't exist, create one.
If it exists, it is a dummy. In that case unDummify() it.
@ -2318,7 +2471,7 @@ MapBlock * ServerMap::emergeBlock(
for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
{
//dstream<<"emergeBlock: x0="<<x0<<", z0="<<z0<<std::endl;
//dstream<<"generateBlock: x0="<<x0<<", z0="<<z0<<std::endl;
float surface_y_f = sector->getGroundHeight(v2s16(x0,z0));
//assert(surface_y_f > GROUNDHEIGHT_VALID_MINVALUE);
@ -2451,23 +2604,25 @@ MapBlock * ServerMap::emergeBlock(
Get local attributes
*/
//dstream<<"emergeBlock(): Getting local attributes"<<std::endl;
//dstream<<"generateBlock(): Getting local attributes"<<std::endl;
float caves_amount = 0;
float caves_amount = 0.5;
#if 0
{
/*
NOTE: BEWARE: Too big amount of attribute points slows verything
down by a lot.
1 interpolation from 5000 points takes 2-3ms.
*/
//TimeTaker timer("emergeBlock() local attribute retrieval");
//TimeTaker timer("generateBlock() local attribute retrieval");
v2s16 nodepos2d = p2d * MAP_BLOCKSIZE;
PointAttributeList *list_caves_amount = m_padb.getList("caves_amount");
caves_amount = list_caves_amount->getInterpolatedFloat(nodepos2d);
}
#endif
//dstream<<"emergeBlock(): Done"<<std::endl;
//dstream<<"generateBlock(): Done"<<std::endl;
/*
Generate dungeons
@ -2617,10 +2772,10 @@ continue_generating:
do_generate_dungeons = false;
}
// Don't generate if mostly underwater surface
else if(mostly_underwater_surface)
/*else if(mostly_underwater_surface)
{
do_generate_dungeons = false;
}
}*/
// Partly underground = cave
else if(!completely_underground)
{
@ -2723,7 +2878,7 @@ continue_generating:
/*
This is used for guessing whether or not the block should
receive sunlight from the top if the top block doesn't exist
receive sunlight from the top if the block above doesn't exist
*/
block->setIsUnderground(completely_underground);
@ -3075,7 +3230,7 @@ continue_generating:
}
else
{
dstream<<"ServerMap::emergeBlock(): "
dstream<<"ServerMap::generateBlock(): "
"Invalid heightmap object"
<<std::endl;
}
@ -3098,6 +3253,148 @@ continue_generating:
{
objects->remove(*i);
}
/*
Translate sector's changed blocks to global changed blocks
*/
for(core::map<s16, MapBlock*>::Iterator
i = changed_blocks_sector.getIterator();
i.atEnd() == false; i++)
{
MapBlock *block = i.getNode()->getValue();
changed_blocks.insert(block->getPos(), block);
}
block->setLightingExpired(true);
#if 0
/*
Debug information
*/
dstream
<<"lighting_invalidated_blocks.size()"
<<", has_dungeons"
<<", completely_ug"
<<", some_part_ug"
<<" "<<lighting_invalidated_blocks.size()
<<", "<<has_dungeons
<<", "<<completely_underground
<<", "<<some_part_underground
<<std::endl;
#endif
return block;
}
MapBlock * ServerMap::emergeBlock(
v3s16 p,
bool only_from_disk,
core::map<v3s16, MapBlock*> &changed_blocks,
core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
)
{
DSTACK("%s: p=(%d,%d,%d), only_from_disk=%d",
__FUNCTION_NAME,
p.X, p.Y, p.Z, only_from_disk);
/*dstream<<"emergeBlock(): "
<<"("<<p.X<<","<<p.Y<<","<<p.Z<<")"
<<std::endl;*/
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*)emergeSector(p2d);
assert(sector->getId() == MAPSECTOR_SERVER);
}
/*catch(InvalidPositionException &e)
{
dstream<<"emergeBlock: emergeSector() failed"<<std::endl;
throw e;
}*/
catch(std::exception &e)
{
dstream<<"emergeBlock: emergeSector() failed: "
<<e.what()<<std::endl;
throw e;
}
/*
Try to get a block from the sector
*/
bool does_not_exist = false;
bool lighting_expired = false;
MapBlock *block = sector->getBlockNoCreateNoEx(block_y);
if(block == NULL)
{
does_not_exist = true;
}
else if(block->isDummy() == true)
{
does_not_exist = true;
}
else if(block->getLightingExpired())
{
lighting_expired = true;
}
else
{
// Valid block
//dstream<<"emergeBlock(): Returning already valid block"<<std::endl;
return block;
}
/*
If block was not found on disk and not going to generate a
new one, make sure there is a dummy block in place.
*/
if(only_from_disk && (does_not_exist || lighting_expired))
{
//dstream<<"emergeBlock(): Was not on disk but not generating"<<std::endl;
if(block == NULL)
{
// Create dummy block
block = new MapBlock(this, p, true);
// Add block to sector
sector->insertBlock(block);
}
// Done.
return block;
}
//dstream<<"Not found on disk, generating."<<std::endl;
// 0ms
//TimeTaker("emergeBlock() generate");
//dstream<<"emergeBlock(): Didn't find valid block -> making one"<<std::endl;
/*
If the block doesn't exist, generate the block.
*/
if(does_not_exist)
{
block = generateBlock(p, block, sector, changed_blocks,
lighting_invalidated_blocks);
}
if(lighting_expired)
{
lighting_invalidated_blocks.insert(p, block);
}
/*
Initially update sunlight
@ -3112,7 +3409,8 @@ continue_generating:
// If sunlight didn't reach everywhere and part of block is
// above ground, lighting has to be properly updated
if(black_air_left && some_part_underground)
//if(black_air_left && some_part_underground)
if(black_air_left)
{
lighting_invalidated_blocks[block->getPos()] = block;
}
@ -3122,37 +3420,7 @@ continue_generating:
lighting_invalidated_blocks[block->getPos()] = block;
}
}
/*
Translate sector's changed blocks to global changed blocks
*/
for(core::map<s16, MapBlock*>::Iterator
i = changed_blocks_sector.getIterator();
i.atEnd() == false; i++)
{
MapBlock *block = i.getNode()->getValue();
changed_blocks.insert(block->getPos(), block);
}
/*
Debug information
*/
if(0)
{
dstream
<<"lighting_invalidated_blocks.size()"
<<", has_dungeons"
<<", completely_ug"
<<", some_part_ug"
<<" "<<lighting_invalidated_blocks.size()
<<", "<<has_dungeons
<<", "<<completely_underground
<<", "<<some_part_underground
<<std::endl;
}
/*
Debug mode operation
*/

107
src/map.h

@ -40,6 +40,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "mapsector.h"
#include "constants.h"
#include "voxel.h"
#include "mapchunk.h"
#define MAPTYPE_BASE 0
#define MAPTYPE_SERVER 1
@ -85,9 +86,14 @@ public:
(float)p.Z * BS + 0.5*BS
);
}
//bool sectorExists(v2s16 p);
// On failure returns NULL
MapSector * getSectorNoGenerateNoExNoLock(v2s16 p2d);
// On failure returns NULL
MapSector * getSectorNoGenerateNoEx(v2s16 p2d);
// On failure throws InvalidPositionException
MapSector * getSectorNoGenerate(v2s16 p2d);
/*
This is overloaded by ClientMap and ServerMap to allow
their differing fetch methods.
@ -318,9 +324,90 @@ public:
}
/*
Forcefully get a sector from somewhere
Map generation
*/
// Returns the position of the chunk where the sector is in
v2s16 sector_to_chunk(v2s16 sectorpos)
{
sectorpos.X += m_chunksize / 2;
sectorpos.Y += m_chunksize / 2;
v2s16 chunkpos = getContainerPos(sectorpos, m_chunksize);
return chunkpos;
}
// Returns the position of the (0,0) sector of the chunk
v2s16 chunk_to_sector(v2s16 chunkpos)
{
v2s16 sectorpos(
chunkpos.X * m_chunksize,
chunkpos.Y * m_chunksize
);
sectorpos.X -= m_chunksize / 2;
sectorpos.Y -= m_chunksize / 2;
return sectorpos;
}
/*
Get a chunk.
*/
MapChunk *getChunk(v2s16 chunkpos)
{
core::map<v2s16, MapChunk*>::Node *n;
n = m_chunks.find(chunkpos);
if(n == NULL)
return NULL;
return n->getValue();
}
/*
Generate a chunk.
All chunks touching this one can be altered also.
Doesn't update lighting.
*/
MapChunk* generateChunkRaw(v2s16 chunkpos);
/*
Generate a chunk and its neighbors so that it won't be touched
anymore.
Doesn't update lighting.
*/
MapChunk* generateChunk(v2s16 chunkpos);
/*
Generate a sector.
This is mainly called by generateChunkRaw.
*/
ServerMapSector * generateSector(v2s16 p);
/*
Get a sector from somewhere.
- Check memory
- Check disk
- Generate chunk
*/
MapSector * emergeSector(v2s16 p);
MapBlock * generateBlock(
v3s16 p,
MapBlock *original_dummy,
ServerMapSector *sector,
core::map<v3s16, MapBlock*> &changed_blocks,
core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
);
MapBlock * emergeBlock(
v3s16 p,
bool only_from_disk,
core::map<v3s16, MapBlock*> &changed_blocks,
core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
);
#if 0
/*
Forcefully get a block from somewhere.
@ -346,7 +433,12 @@ public:
core::map<v3s16, MapBlock*> &changed_blocks,
core::map<v3s16, MapBlock*> &lighting_invalidated_blocks
);
#endif
/*
Misc. helper functions for fiddling with directory and file
names when saving
*/
void createDir(std::string path);
void createSaveDir();
// returns something like "xxxxxxxx"
@ -396,8 +488,17 @@ private:
std::string m_savedir;
bool m_map_saving_enabled;
// Chunk size in MapSectors
s16 m_chunksize;
// Chunks
core::map<v2s16, MapChunk*> m_chunks;
};
/*
ClientMap stuff
*/
#ifndef SERVER
struct MapDrawControl

@ -293,7 +293,8 @@ void MapBlock::makeFastFace(TileSpec tile, u8 light, v3f p,
Gets node tile from any place relative to block.
Returns TILE_NODE if doesn't exist or should not be drawn.
*/
TileSpec MapBlock::getNodeTile(MapNode mn, v3s16 p, v3s16 face_dir)
TileSpec MapBlock::getNodeTile(MapNode mn, v3s16 p, v3s16 face_dir,
NodeModMap &temp_mods)
{
TileSpec spec;
spec = mn.getTile(face_dir);
@ -301,13 +302,15 @@ TileSpec MapBlock::getNodeTile(MapNode mn, v3s16 p, v3s16 face_dir)
/*
Check temporary modifications on this node
*/
core::map<v3s16, NodeMod>::Node *n;
/*core::map<v3s16, NodeMod>::Node *n;
n = m_temp_mods.find(p);
// If modified
if(n != NULL)
{
struct NodeMod mod = n->getValue();
struct NodeMod mod = n->getValue();*/
NodeMod mod;
if(temp_mods.get(p, &mod))
{
if(mod.type == NODEMOD_CHANGECONTENT)
{
MapNode mn2(mod.param);
@ -326,18 +329,20 @@ TileSpec MapBlock::getNodeTile(MapNode mn, v3s16 p, v3s16 face_dir)
return spec;
}
u8 MapBlock::getNodeContent(v3s16 p, MapNode mn)
u8 MapBlock::getNodeContent(v3s16 p, MapNode mn, NodeModMap &temp_mods)
{
/*
Check temporary modifications on this node
*/
core::map<v3s16, NodeMod>::Node *n;
/*core::map<v3s16, NodeMod>::Node *n;
n = m_temp_mods.find(p);
// If modified
if(n != NULL)
{
struct NodeMod mod = n->getValue();
struct NodeMod mod = n->getValue();*/
NodeMod mod;
if(temp_mods.get(p, &mod))
{
if(mod.type == NODEMOD_CHANGECONTENT)
{
// Overrides content
@ -376,7 +381,8 @@ void MapBlock::updateFastFaceRow(
v3f translate_dir_f,
v3s16 face_dir,
v3f face_dir_f,
core::array<FastFace> &dest)
core::array<FastFace> &dest,
NodeModMap &temp_mods)
{
v3s16 p = startpos;
@ -387,8 +393,8 @@ void MapBlock::updateFastFaceRow(
u8 light = getFaceLight(daynight_ratio, n0, n1, face_dir);
TileSpec tile0 = getNodeTile(n0, p, face_dir);
TileSpec tile1 = getNodeTile(n1, p + face_dir, -face_dir);
TileSpec tile0 = getNodeTile(n0, p, face_dir, temp_mods);
TileSpec tile1 = getNodeTile(n1, p + face_dir, -face_dir, temp_mods);
for(u16 j=0; j<length; j++)
{
@ -406,8 +412,8 @@ void MapBlock::updateFastFaceRow(
p_next = p + translate_dir;
n0_next = getNodeParentNoEx(p_next);
n1_next = getNodeParentNoEx(p_next + face_dir);
tile0_next = getNodeTile(n0_next, p_next, face_dir);
tile1_next = getNodeTile(n1_next, p_next + face_dir, -face_dir);
tile0_next = getNodeTile(n0_next, p_next, face_dir, temp_mods);
tile1_next = getNodeTile(n1_next,p_next+face_dir,-face_dir, temp_mods);
light_next = getFaceLight(daynight_ratio, n0_next, n1_next, face_dir);
if(tile0_next == tile0
@ -427,8 +433,8 @@ void MapBlock::updateFastFaceRow(
*/
//u8 mf = face_contents(tile0, tile1);
// This is hackish
u8 content0 = getNodeContent(p, n0);
u8 content1 = getNodeContent(p + face_dir, n1);
u8 content0 = getNodeContent(p, n0, temp_mods);
u8 content1 = getNodeContent(p + face_dir, n1, temp_mods);
u8 mf = face_contents(content0, content1);
if(mf != 0)
@ -594,6 +600,16 @@ void MapBlock::updateMesh(u32 daynight_ratio)
v3f posRelative_f(getPosRelative().X, getPosRelative().Y,
getPosRelative().Z); // floating point conversion
/*
Avoid interlocks by copying m_temp_mods
*/
NodeModMap temp_mods;
{
JMutexAutoLock lock(m_temp_mods_mutex);
m_temp_mods.copy(temp_mods);
}
/*
We are including the faces of the trailing edges of the block.
This means that when something changes, the caller must
@ -606,9 +622,6 @@ void MapBlock::updateMesh(u32 daynight_ratio)
// 4-23ms for MAP_BLOCKSIZE=16
//TimeTaker timer2("updateMesh() collect");
// Lock this, as m_temp_mods will be used directly
JMutexAutoLock lock(m_temp_mods_mutex);
/*
Go through every y,z and get top faces in rows of x+
*/
@ -620,7 +633,8 @@ void MapBlock::updateMesh(u32 daynight_ratio)
v3f (1,0,0),
v3s16(0,1,0), //face dir
v3f (0,1,0),
fastfaces_new);
fastfaces_new,
temp_mods);
}
}
/*
@ -634,7 +648,8 @@ void MapBlock::updateMesh(u32 daynight_ratio)
v3f (0,0,1),
v3s16(1,0,0),
v3f (1,0,0),
fastfaces_new);
fastfaces_new,
temp_mods);
}
}
/*
@ -648,7 +663,8 @@ void MapBlock::updateMesh(u32 daynight_ratio)
v3f (1,0,0),
v3s16(0,0,1),
v3f (0,0,1),
fastfaces_new);
fastfaces_new,
temp_mods);
}
}
}
@ -1297,10 +1313,6 @@ void MapBlock::copyTo(VoxelManipulator &dst)
getPosRelative(), data_size);
}
/*void getPseudoObjects(v3f origin, f32 max_d,
core::array<DistanceSortedObject> &dest)
{
}*/
void MapBlock::stepObjects(float dtime, bool server, u32 daynight_ratio)
{
/*
@ -1501,6 +1513,8 @@ void MapBlock::serialize(std::ostream &os, u8 version)
flags |= 1;
if(m_day_night_differs)
flags |= 2;
if(m_lighting_expired)
flags |= 3;
os.write((char*)&flags, 1);
u32 nodecount = MAP_BLOCKSIZE*MAP_BLOCKSIZE*MAP_BLOCKSIZE;
@ -1622,6 +1636,7 @@ void MapBlock::deSerialize(std::istream &is, u8 version)
is.read((char*)&flags, 1);
is_underground = (flags & 1) ? true : false;
m_day_night_differs = (flags & 2) ? true : false;
m_lighting_expired = (flags & 3) ? true : false;
// Uncompress data
std::ostringstream os(std::ios_base::binary);

@ -70,6 +70,74 @@ struct NodeMod
u16 param;
};
class NodeModMap
{
public:
/*
returns true if the mod was different last time
*/
bool set(v3s16 p, const NodeMod &mod)
{
// See if old is different, cancel if it is not different.
core::map<v3s16, NodeMod>::Node *n = m_mods.find(p);
if(n)
{
NodeMod old = n->getValue();
if(old == mod)
return false;
n->setValue(mod);
}
else
{
m_mods.insert(p, mod);
}
return true;
}
// Returns true if there was one
bool get(v3s16 p, NodeMod *mod)
{
core::map<v3s16, NodeMod>::Node *n;
n = m_mods.find(p);
if(n == NULL)
return false;
if(mod)
*mod = n->getValue();
return true;
}
bool clear(v3s16 p)
{
if(m_mods.find(p))
{
m_mods.remove(p);
return true;
}
return false;
}
bool clear()
{
if(m_mods.size() == 0)
return false;
m_mods.clear();
return true;
}
void copy(NodeModMap &dest)
{
dest.m_mods.clear();
for(core::map<v3s16, NodeMod>::Iterator
i = m_mods.getIterator();
i.atEnd() == false; i++)
{
dest.m_mods.insert(i.getNode()->getKey(), i.getNode()->getValue());
}
}
private:
core::map<v3s16, NodeMod> m_mods;
};
enum
{
NODECONTAINER_ID_MAPBLOCK,
@ -104,71 +172,6 @@ public:
return m_parent;
}
bool isDummy()
{
return (data == NULL);
}
void unDummify()
{
assert(isDummy());
reallocate();
}
bool getChangedFlag()
{
return changed;
}
void resetChangedFlag()
{
changed = false;
}
void setChangedFlag()
{
changed = true;
}
#ifndef SERVER
void setMeshExpired(bool expired)
{
m_mesh_expired = expired;
}
bool getMeshExpired()
{
return m_mesh_expired;
}
#endif
v3s16 getPos()
{
return m_pos;
}
v3s16 getPosRelative()
{
return m_pos * MAP_BLOCKSIZE;
}
bool getIsUnderground()
{
return is_underground;
}
void setIsUnderground(bool a_is_underground)
{
is_underground = a_is_underground;
setChangedFlag();
}
core::aabbox3d<s16> getBox()
{
return core::aabbox3d<s16>(getPosRelative(),
getPosRelative()
+ v3s16(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE)
- v3s16(1,1,1));
}
void reallocate()
{
if(data != NULL)
@ -181,6 +184,101 @@ public:
setChangedFlag();
}
/*
Flags
*/
bool isDummy()
{
return (data == NULL);
}
void unDummify()
{
assert(isDummy());
reallocate();
}
bool getChangedFlag()
{
return changed;
}
void resetChangedFlag()
{
changed = false;
}
void setChangedFlag()
{
changed = true;
}
bool getIsUnderground()
{
return is_underground;
}
void setIsUnderground(bool a_is_underground)
{
is_underground = a_is_underground;
setChangedFlag();
}
#ifndef SERVER
void setMeshExpired(bool expired)
{
m_mesh_expired = expired;
}
bool getMeshExpired()
{
return m_mesh_expired;
}
#endif
void setLightingExpired(bool expired)
{
m_lighting_expired = expired;
setChangedFlag();
}
bool getLightingExpired()
{
return m_lighting_expired;
}
bool isValid()
{
if(m_lighting_expired)
return false;
if(data == NULL)
return false;
return true;
}
/*
Position stuff
*/
v3s16 getPos()
{
return m_pos;
}
v3s16 getPosRelative()
{
return m_pos * MAP_BLOCKSIZE;
}
core::aabbox3d<s16> getBox()
{
return core::aabbox3d<s16>(getPosRelative(),
getPosRelative()
+ v3s16(MAP_BLOCKSIZE, MAP_BLOCKSIZE, MAP_BLOCKSIZE)
- v3s16(1,1,1));
}
/*
Regular MapNode get-setters
*/
bool isValidPosition(v3s16 p)
{
if(data == NULL)
@ -190,10 +288,6 @@ public:
&& p.Z >= 0 && p.Z < MAP_BLOCKSIZE);
}
/*
Regular MapNode get-setters
*/
MapNode getNode(s16 x, s16 y, s16 z)
{
if(data == NULL)
@ -271,9 +365,14 @@ public:
setNode(x0+x, y0+y, z0+z, node);
}
/*
Graphics-related methods
*/
// A quick version with nodes passed as parameters
u8 getFaceLight(u32 daynight_ratio, MapNode n, MapNode n2,
v3s16 face_dir);
// A more convenient version
u8 getFaceLight(u32 daynight_ratio, v3s16 p, v3s16 face_dir)
{
return getFaceLight(daynight_ratio,
@ -288,11 +387,16 @@ public:
v3s16 dir, v3f scale, v3f posRelative_f,
core::array<FastFace> &dest);
TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 face_dir);
u8 getNodeContent(v3s16 p, MapNode mn);
TileSpec getNodeTile(MapNode mn, v3s16 p, v3s16 face_dir,
NodeModMap &temp_mods);
u8 getNodeContent(v3s16 p, MapNode mn,
NodeModMap &temp_mods);
/*
startpos:
Generates the FastFaces of a node row. This has a
ridiculous amount of parameters because that way they
can be precalculated by the caller.
translate_dir: unit vector with only one of x, y or z
face_dir: unit vector with only one of x, y or z
*/
@ -305,12 +409,14 @@ public:
v3f translate_dir_f,
v3s16 face_dir,
v3f face_dir_f,
core::array<FastFace> &dest);
core::array<FastFace> &dest,
NodeModMap &temp_mods);
/*
Thread-safely updates the whole mesh of the mapblock.
*/
void updateMesh(u32 daynight_ratio);
/*void updateMesh(s32 daynight_i);
// Updates all DAYNIGHT_CACHE_COUNT meshes
void updateMeshes(s32 first_i=0);*/
#endif // !SERVER
// See comments in mapblock.cpp
@ -322,7 +428,7 @@ public:
void copyTo(VoxelManipulator &dst);
/*
Object stuff
MapBlockObject stuff
*/
void serializeObjects(std::ostream &os, u8 version)
@ -384,9 +490,6 @@ public:
m_objects.getObjects(origin, max_d, dest);
}
/*void getPseudoObjects(v3f origin, f32 max_d,
core::array<DistanceSortedObject> &dest);*/
s32 getObjectCount()
{
return m_objects.getCount();
@ -399,7 +502,7 @@ public:
returns true if the mod was different last time
*/
bool setTempMod(v3s16 p, NodeMod mod)
bool setTempMod(v3s16 p, const NodeMod &mod)
{
/*dstream<<"setTempMod called on block"
<<" ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
@ -408,52 +511,33 @@ public:
<<std::endl;*/
JMutexAutoLock lock(m_temp_mods_mutex);
// See if old is different, cancel if it is not different.
core::map<v3s16, NodeMod>::Node *n = m_temp_mods.find(p);
if(n)
{
NodeMod old = n->getValue();
if(old == mod)
return false;
}
m_temp_mods[p] = mod;
return true;
return m_temp_mods.set(p, mod);
}
// Returns true if there was one
bool getTempMod(v3s16 p, struct NodeMod *mod)
bool getTempMod(v3s16 p, NodeMod *mod)
{
JMutexAutoLock lock(m_temp_mods_mutex);
core::map<v3s16, NodeMod>::Node *n;
n = m_temp_mods.find(p);
if(n == NULL)
return false;
if(mod)
*mod = n->getValue();
return true;
return m_temp_mods.get(p, mod);
}
bool clearTempMod(v3s16 p)
{
JMutexAutoLock lock(m_temp_mods_mutex);
if(m_temp_mods.find(p))
{
m_temp_mods.remove(p);
return true;
}
return false;
return m_temp_mods.clear(p);
}
bool clearTempMods()
{
JMutexAutoLock lock(m_temp_mods_mutex);
if(m_temp_mods.size() == 0)
return false;
m_temp_mods.clear();
return true;
return m_temp_mods.clear();
}
#endif
/*
Day-night lighting difference
Update day-night lighting difference flag.
Sets m_day_night_differs to appropriate value.
These methods don't care about neighboring blocks.
It means that to know if a block really doesn't need a mesh
@ -490,17 +574,10 @@ public:
void deSerialize(std::istream &is, u8 version);
/*
Public member variables
*/
#ifndef SERVER
//scene::SMesh *mesh[DAYNIGHT_CACHE_COUNT];
scene::SMesh *mesh;
JMutex mesh_mutex;
#endif
private:
/*
Private methods
*/
/*
Used only internally, because changes can't be tracked
@ -520,28 +597,58 @@ private:
return getNodeRef(p.X, p.Y, p.Z);
}
public:
/*
Public member variables
*/
#ifndef SERVER
scene::SMesh *mesh;
JMutex mesh_mutex;
#endif
private:
/*
Private member variables
*/
// Parent container (practically the Map)
// Not a MapSector, it is just a structural element.
NodeContainer *m_parent;
// Position in blocks on parent
v3s16 m_pos;
/*
If NULL, block is a dummy block.
Dummy blocks are used for caching not-found-on-disk blocks.
*/
MapNode * data;
/*
- On the client, this is used for checking whether to
recalculate the face cache. (Is it anymore?)
- On the server, this is used for telling whether the
block has been changed from the one on disk.
- On the client, this is used for nothing.
*/
bool changed;
/*
Used for some initial lighting stuff.
At least /has been/ used. 8)
It's probably useless now.
When propagating sunlight and the above block doesn't exist,
sunlight is assumed if this is false.
In practice this is set to true if the block is completely
undeground with nothing visible above the ground except
caves.
*/
bool is_underground;
/*
Set to true if changes has been made that make the old lighting
values wrong but the lighting hasn't been actually updated.
If this is false, lighting is exactly right.
If this is true, lighting might be wrong or right.
*/
bool m_lighting_expired;
// Whether day and night lighting differs
bool m_day_night_differs;
@ -552,11 +659,16 @@ private:
float m_spawn_timer;
#ifndef SERVER
/*
Set to true if the mesh has been ordered to be updated
sometime in the background.
In practice this is set when the day/night lighting switches.
*/
bool m_mesh_expired;
// Temporary modifications to nodes
// These are only used when drawing
core::map<v3s16, NodeMod> m_temp_mods;
NodeModMap m_temp_mods;
JMutex m_temp_mods_mutex;
#endif
};

51
src/mapchunk.h Normal file

@ -0,0 +1,51 @@
/*
Minetest-c55
Copyright (C) 2010 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(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
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef MAPCHUNK_HEADER
#define MAPCHUNK_HEADER
/*
MapChunk contains map-generation-time metadata for an area of
some MapSectors. (something like 64x64)
*/
class MapChunk
{
public:
MapChunk():
m_is_volatile(true)
{
}
/*
If is_volatile is true, chunk can be modified when
neighboring chunks are generated.
It is set to false when all the 8 neighboring chunks have
been generated.
*/
bool getIsVolatile(){ return m_is_volatile; }
void setIsVolatile(bool is){ m_is_volatile = is; }
private:
bool m_is_volatile;
};
#endif

@ -82,11 +82,16 @@ MapBlock * MapSector::getBlockBuffered(s16 y)
return block;
}
MapBlock * MapSector::getBlockNoCreate(s16 y)
MapBlock * MapSector::getBlockNoCreateNoEx(s16 y)
{
JMutexAutoLock lock(m_mutex);
MapBlock *block = getBlockBuffered(y);
return getBlockBuffered(y);
}
MapBlock * MapSector::getBlockNoCreate(s16 y)
{
MapBlock *block = getBlockNoCreateNoEx(y);
if(block == NULL)
throw InvalidPositionException();

@ -67,6 +67,7 @@ public:
return m_pos;
}
MapBlock * getBlockNoCreateNoEx(s16 y);
MapBlock * getBlockNoCreate(s16 y);
MapBlock * createBlankBlockNoInsert(s16 y);
MapBlock * createBlankBlock(s16 y);

@ -156,20 +156,18 @@ void * EmergeThread::Thread()
only_from_disk = true;
// First check if the block already exists
if(only_from_disk)
{
block = map.getBlockNoCreate(p);
}
//block = map.getBlockNoCreate(p);
if(block == NULL)
{
//dstream<<"Calling emergeBlock"<<std::endl;
block = map.emergeBlock(
p,
only_from_disk,
changed_blocks,
lighting_invalidated_blocks);
#if 1
#if 0
/*
EXPERIMENTAL: Create a few other blocks too
*/
@ -206,6 +204,12 @@ void * EmergeThread::Thread()
{
//dstream<<"EmergeThread: Got a dummy block"<<std::endl;
got_block = false;
if(only_from_disk == false)
{
dstream<<"EmergeThread: wanted to generate a block but got a dummy"<<std::endl;
assert(0);
}
}
}
catch(InvalidPositionException &e)
@ -581,16 +585,10 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
/*
Check if map has this block
*/
MapBlock *block = NULL;
try
{
block = server->m_env.getMap().getBlockNoCreate(p);
}
catch(InvalidPositionException &e)
{
}
MapBlock *block = server->m_env.getMap().getBlockNoCreateNoEx(p);
bool surely_not_found_on_disk = false;
bool block_is_invalid = false;
if(block != NULL)
{
/*if(block->isIncomplete())
@ -603,6 +601,11 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
{
surely_not_found_on_disk = true;
}
if(block->isValid() == false)
{
block_is_invalid = true;
}
}
/*
@ -627,8 +630,10 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
/*
Add inexistent block to emerge queue.
*/
if(block == NULL || surely_not_found_on_disk)
if(block == NULL || surely_not_found_on_disk || block_is_invalid)
{
//dstream<<"asd"<<std::endl;
/*SharedPtr<JMutexAutoLock> lock
(m_num_blocks_in_emerge_queue.getLock());*/
@ -636,6 +641,8 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime,
// Allow only one block in emerge queue
if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
{
//dstream<<"Adding block to emerge queue"<<std::endl;
// Add it to the emerge queue and trigger the thread
u8 flags = 0;

@ -538,6 +538,23 @@ inline v3s16 getContainerPos(v3s16 p, s16 d)
);
}
inline v2s16 getContainerPos(v2s16 p, v2s16 d)
{
return v2s16(
getContainerPos(p.X, d.X),
getContainerPos(p.Y, d.Y)
);
}
inline v3s16 getContainerPos(v3s16 p, v3s16 d)
{
return v3s16(
getContainerPos(p.X, d.X),
getContainerPos(p.Y, d.Y),
getContainerPos(p.Z, d.Z)
);
}
inline bool isInArea(v3s16 p, s16 d)
{
return (

@ -536,10 +536,13 @@ void VoxelManipulator::spreadLight(enum LightBank bank, v3s16 p)
}
}
#if 1
#if 0
/*
Lights neighbors of from_nodes, collects all them and then
goes on recursively.
NOTE: This is faster in small areas but will overflow the
stack on large areas. Thus it is not used.
*/
void VoxelManipulator::spreadLight(enum LightBank bank,
core::map<v3s16, bool> & from_nodes)
@ -560,7 +563,7 @@ void VoxelManipulator::spreadLight(enum LightBank bank,
}
#endif
#if 0
#if 1
/*
Lights neighbors of from_nodes, collects all them and then
goes on recursively.