1
0

Thread-safe chunk generation, storage and generator are queried for progress while initializing server

Note that this commit breaks foliage generation - there are no trees in the chunks generated!

git-svn-id: http://mc-server.googlecode.com/svn/trunk@292 0a769ca7-a7f5-676a-18bf-c427514a06d6
This commit is contained in:
madmaxoft@gmail.com
2012-02-18 17:53:22 +00:00
parent 57dedd585c
commit 499745c1c7
16 changed files with 407 additions and 175 deletions

View File

@@ -91,6 +91,55 @@ inline float fRadRand( float a_Radius )
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cWorldLoadProgress:
/// A simple thread that displays the progress of world loading / saving in cWorld::InitializeSpawn()
class cWorldLoadProgress :
public cIsThread
{
public:
cWorldLoadProgress(cWorld * a_World) :
cIsThread("cWorldLoadProgress"),
m_World(a_World)
{
Start();
}
protected:
cWorld * m_World;
virtual void Execute(void) override
{
for (;;)
{
LOG("%d chunks to load, %d chunks to generate",
m_World->GetStorage().GetLoadQueueLength(),
m_World->GetGenerator().GetQueueLength()
);
// Wait for 2 sec, but be "reasonably wakeable" when the thread is to finish
for (int i = 0; i < 20; i++)
{
cSleep::MilliSleep(100);
if (mShouldTerminate)
{
return;
}
}
} // for (-ever)
}
} ;
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cWorld:
cWorld* cWorld::GetWorld()
{
LOGWARN("WARNING: Using deprecated function cWorld::GetWorld() use cRoot::Get()->GetWorld() instead!");
@@ -154,7 +203,7 @@ cWorld::cWorld( const AString & a_WorldName )
m_GameMode = 0;
AString GeneratorName;
AString StorageSchema;
AString StorageSchema("Default");
cIniFile IniFile( m_WorldName + "/world.ini");
if( IniFile.ReadFile() )
@@ -164,8 +213,8 @@ cWorld::cWorld( const AString & a_WorldName )
m_SpawnZ = IniFile.GetValueF("SpawnPosition", "Z", m_SpawnZ );
m_WorldSeed = IniFile.GetValueI("Seed", "Seed", m_WorldSeed );
m_GameMode = IniFile.GetValueI("GameMode", "GameMode", m_GameMode );
GeneratorName = IniFile.GetValue("Generator", "GeneratorName", "Default");
StorageSchema = IniFile.GetValue("Storage", "Schema", "Default");
GeneratorName = IniFile.GetValue("Generator", "GeneratorName", GeneratorName);
StorageSchema = IniFile.GetValue("Storage", "Schema", StorageSchema);
}
else
{
@@ -174,7 +223,8 @@ cWorld::cWorld( const AString & a_WorldName )
IniFile.SetValueF("SpawnPosition", "Z", m_SpawnZ );
IniFile.SetValueI("Seed", "Seed", m_WorldSeed );
IniFile.SetValueI("GameMode", "GameMode", m_GameMode );
IniFile.SetValue("Generator", "GeneratorName", "Default" );
IniFile.SetValue("Generator", "GeneratorName", GeneratorName);
IniFile.SetValue("Storage", "Schema", StorageSchema);
if( !IniFile.WriteFile() )
{
LOG("WARNING: Could not write to %s/world.ini", a_WorldName.c_str());
@@ -380,7 +430,9 @@ void cWorld::InitializeSpawn()
{
int ChunkX = 0, ChunkY = 0, ChunkZ = 0;
BlockToChunk( (int)m_SpawnX, (int)m_SpawnY, (int)m_SpawnZ, ChunkX, ChunkY, ChunkZ );
int ViewDist = 20; // Always prepare an area 20 chunks across, no matter what the actual cClientHandle::VIEWDISTANCE is
LOG("Preparing spawn area in world \"%s\"", m_WorldName.c_str());
for (int x = 0; x < ViewDist; x++)
{
@@ -390,7 +442,16 @@ void cWorld::InitializeSpawn()
}
}
// TODO: Wait for the generator to finish generating these chunks
// Display progress during this process:
cWorldLoadProgress Progress(this);
// Wait for the loader to finish loading
m_Storage.WaitForQueuesEmpty();
// Wait for the generator to finish generating
m_Generator.WaitForQueueEmpty();
m_SpawnY = (double)GetHeight( (int)m_SpawnX, (int)m_SpawnZ ) + 1.6f; // +1.6f eye height
}
@@ -434,23 +495,7 @@ void cWorld::Tick(float a_Dt)
}
}
{
cCSLock Lock(m_CSLighting);
if (m_SpreadQueue.size() >= 50 )
{
LOGWARN("cWorld: Lots of lighting to do! Still %i chunks left!", m_SpreadQueue.size() );
}
int TimesSpreaded = 0;
while ( !m_SpreadQueue.empty() && TimesSpreaded < MAX_LIGHTING_SPREAD_PER_TICK ) // Do not choke the tick thread
{
cChunkPtr & Chunk = *m_SpreadQueue.begin();
//LOG("Spreading: %p", Chunk );
Chunk->SpreadLight( Chunk->pGetSkyLight() );
Chunk->SpreadLight( Chunk->pGetLight() );
m_SpreadQueue.pop_front();
TimesSpreaded++;
}
}
TickLighting();
m_ChunkMap->Tick(a_Dt, m_TickRand);
@@ -602,7 +647,7 @@ void cWorld::TickSpawnMobs(float a_Dt)
int nightRand = m_TickRand.randInt() % 10;
SpawnPos += Vector3d( (double)(m_TickRand.randInt() % 64) - 32, (double)(m_TickRand.randInt() % 64) - 32, (double)(m_TickRand.randInt() % 64) - 32 );
char Height = GetHeight( (int)SpawnPos.x, (int)SpawnPos.z );
int Height = GetHeight( (int)SpawnPos.x, (int)SpawnPos.z );
if (m_WorldTime >= 12000 + 1000)
{
@@ -657,6 +702,37 @@ void cWorld::TickSpawnMobs(float a_Dt)
void cWorld::TickLighting(void)
{
// To avoid a deadlock, we lock the spread queue only long enough to pick the chunk coords to spread
// The spreading itself will run unlocked
cChunkCoordsList SpreadQueue;
{
cCSLock Lock(m_CSLighting);
if (m_SpreadQueue.size() == 0)
{
return;
}
if (m_SpreadQueue.size() >= MAX_LIGHTING_SPREAD_PER_TICK )
{
LOGWARN("cWorld: Lots of lighting to do! Still %i chunks left!", m_SpreadQueue.size() );
}
// Move up to MAX_LIGHTING_SPREAD_PER_TICK elements from m_SpreadQueue out into SpreadQueue:
cChunkCoordsList::iterator itr = m_SpreadQueue.begin();
std::advance(itr, MIN(m_SpreadQueue.size(), MAX_LIGHTING_SPREAD_PER_TICK));
SpreadQueue.splice(SpreadQueue.begin(), m_SpreadQueue, m_SpreadQueue.begin(), itr);
}
for (cChunkCoordsList::iterator itr = SpreadQueue.begin(); itr != SpreadQueue.end(); ++itr)
{
m_ChunkMap->SpreadChunkLighting(itr->m_ChunkX, itr->m_ChunkY, itr->m_ChunkZ);
}
}
void cWorld::GrowTree( int a_X, int a_Y, int a_Z )
{
// new tree code, looks much better
@@ -896,16 +972,9 @@ cBlockEntity * cWorld::GetBlockEntity( int a_X, int a_Y, int a_Z )
char cWorld::GetHeight( int a_X, int a_Z )
int cWorld::GetHeight( int a_X, int a_Z )
{
int PosX = a_X, PosY = 0, PosZ = a_Z, ChunkX, ChunkY, ChunkZ;
AbsoluteToRelative( PosX, PosY, PosZ, ChunkX, ChunkY, ChunkZ );
cChunkPtr Chunk = GetChunk( ChunkX, ChunkY, ChunkZ );
if ( Chunk->IsValid())
{
return Chunk->GetHeight( PosX, PosZ );
}
return 0;
return m_ChunkMap->GetHeight(a_X, a_Z);
}
@@ -914,7 +983,6 @@ char cWorld::GetHeight( int a_X, int a_Z )
const double & cWorld::GetSpawnY(void)
{
m_SpawnY = (double)GetHeight( (int)m_SpawnX, (int)m_SpawnZ ) + 1.6f; // +1.6f eye height
return m_SpawnY;
}
@@ -983,9 +1051,9 @@ void cWorld::ChunkDataLoaded(int a_ChunkX, int a_ChunkY, int a_ChunkZ, const cha
void cWorld::SetChunkData(int a_ChunkX, int a_ChunkY, int a_ChunkZ, const char * a_BlockData, cEntityList & a_Entities, cBlockEntityList & a_BlockEntities)
void cWorld::ChunkDataGenerated(int a_ChunkX, int a_ChunkY, int a_ChunkZ, const char * a_BlockData, cEntityList & a_Entities, cBlockEntityList & a_BlockEntities)
{
m_ChunkMap->SetChunkData(a_ChunkX, a_ChunkY, a_ChunkZ, a_BlockData, a_Entities, a_BlockEntities);
m_ChunkMap->ChunkDataGenerated(a_ChunkX, a_ChunkY, a_ChunkZ, a_BlockData, a_Entities, a_BlockEntities);
}
@@ -1217,21 +1285,21 @@ void cWorld::SaveAllChunks()
void cWorld::ReSpreadLighting( const cChunkPtr & a_Chunk )
void cWorld::ReSpreadLighting(int a_ChunkX, int a_ChunkY, int a_ChunkZ)
{
cCSLock Lock(m_CSLighting);
m_SpreadQueue.remove( a_Chunk );
m_SpreadQueue.push_back( a_Chunk );
m_SpreadQueue.remove(cChunkCoords(a_ChunkX, a_ChunkY, a_ChunkZ));
m_SpreadQueue.push_back(cChunkCoords(a_ChunkX, a_ChunkY, a_ChunkZ));
}
void cWorld::RemoveSpread( const cChunkPtr & a_Chunk )
void cWorld::RemoveSpread(int a_ChunkX, int a_ChunkY, int a_ChunkZ)
{
cCSLock Lock(m_CSLighting);
m_SpreadQueue.remove( a_Chunk );
m_SpreadQueue.remove(cChunkCoords(a_ChunkX, a_ChunkY, a_ChunkZ));
}