From 4f17362aeb80e5339c58a5d3b0fbaeb88d9e701c Mon Sep 17 00:00:00 2001 From: "madmaxoft@gmail.com" Date: Mon, 13 Feb 2012 21:47:03 +0000 Subject: Rewritten most of the code for multithreading; still not 100%, but getting there. If this commit proves to be too problematic, we can always undo it. git-svn-id: http://mc-server.googlecode.com/svn/trunk@251 0a769ca7-a7f5-676a-18bf-c427514a06d6 --- source/cWorld.cpp | 855 ++++++++++++++++++++++++++---------------------------- 1 file changed, 404 insertions(+), 451 deletions(-) (limited to 'source/cWorld.cpp') diff --git a/source/cWorld.cpp b/source/cWorld.cpp index 00dcbd80e..c007070c6 100644 --- a/source/cWorld.cpp +++ b/source/cWorld.cpp @@ -40,14 +40,13 @@ #include "cChunkGenerator.h" #include "MersenneTwister.h" #include "cWorldGenerator_Test.h" +#include "cTracer.h" #include "packets/cPacket_TimeUpdate.h" #include "packets/cPacket_NewInvalidState.h" #include "packets/cPacket_Thunderbolt.h" -#include "ptr_cChunk.h" - #include "Vector3d.h" #include @@ -62,6 +61,13 @@ +/// Up to this many m_SpreadQueue elements are handled each world tick +const int MAX_LIGHTING_SPREAD_PER_TICK = 10; + + + + + float cWorld::m_Time = 0.f; char g_BlockLightValue[128]; @@ -70,6 +76,10 @@ bool g_BlockTransparent[128]; bool g_BlockOneHitDig[128]; bool g_BlockPistonBreakable[128]; + + + + #define RECI_RAND_MAX (1.f/RAND_MAX) inline float fRadRand( float a_Radius ) { @@ -77,36 +87,9 @@ inline float fRadRand( float a_Radius ) return ((float)r1.rand() * RECI_RAND_MAX)*a_Radius - a_Radius*0.5f; } -struct sSetBlockData -{ - sSetBlockData( int a_X, int a_Y, int a_Z, char a_BlockID, char a_BlockMeta ) - : x( a_X ) - , y( a_Y ) - , z( a_Z ) - , BlockID( a_BlockID ) - , BlockMeta( a_BlockMeta ) - {} - int x, y, z; - char BlockID, BlockMeta; -}; - -typedef std::list< sSetBlockData > FastSetBlockList; - -struct cWorld::sWorldState -{ - cWorld::EntityList RemoveEntityQueue; - cWorld::EntityList AllEntities; - cWorld::ClientList Clients; - cWorld::PlayerList Players; - cWorld::ChunkList SpreadQueue; - FastSetBlockList FastSetBlockQueue; - cChunkGenerator* pChunkGenerator; - - std::string WorldName; -}; cWorld* cWorld::GetWorld() { @@ -120,15 +103,19 @@ cWorld* cWorld::GetWorld() cWorld::~cWorld() { - LockEntities(); - while( m_pState->AllEntities.begin() != m_pState->AllEntities.end() ) { - cEntity* Entity = *m_pState->AllEntities.begin(); - m_pState->AllEntities.remove( Entity ); - if( !Entity->IsDestroyed() ) Entity->Destroy(); - RemoveEntity( Entity ); + cCSLock Lock(m_CSEntities); + while( m_AllEntities.begin() != m_AllEntities.end() ) + { + cEntity* Entity = *m_AllEntities.begin(); + m_AllEntities.remove( Entity ); + if ( !Entity->IsDestroyed() ) + { + Entity->Destroy(); + } + delete Entity; + } } - UnlockEntities(); delete m_SimulatorManager; delete m_SandSimulator; @@ -136,33 +123,28 @@ cWorld::~cWorld() delete m_LavaSimulator; delete m_FireSimulator; - UnloadUnusedChunks(); - delete m_pState->pChunkGenerator; - delete m_ChunkMap; + m_Generator.Stop(); - delete m_ClientHandleCriticalSection; m_ClientHandleCriticalSection = 0; - delete m_EntitiesCriticalSection; m_EntitiesCriticalSection = 0; - delete m_ChunksCriticalSection; m_ChunksCriticalSection = 0; - delete m_pState; + UnloadUnusedChunks(); + + m_Storage.WaitForFinish(); - delete m_WorldGenerator; + delete m_ChunkMap; } -cWorld::cWorld( const char* a_WorldName ) - : m_pState( new sWorldState ) - , m_SpawnMonsterTime( 0.f ) +cWorld::cWorld( const AString & a_WorldName ) + : m_SpawnMonsterTime( 0.f ) , m_RSList ( 0 ) , m_Weather ( 0 ) - , m_WorldGenerator( 0 ) { - LOG("cWorld::cWorld(%s)", a_WorldName); - m_pState->WorldName = a_WorldName; + LOG("cWorld::cWorld(%s)", a_WorldName.c_str()); + m_WorldName = a_WorldName; - cMakeDir::MakeDir(m_pState->WorldName.c_str()); + cMakeDir::MakeDir(m_WorldName.c_str()); MTRand r1; m_SpawnX = (double)((r1.randInt()%1000)-500); @@ -171,9 +153,10 @@ cWorld::cWorld( const char* a_WorldName ) m_WorldSeed = r1.randInt(); m_GameMode = 0; - std::string WorldGeneratorName; + AString GeneratorName; + AString StorageSchema; - cIniFile IniFile( m_pState->WorldName + "/world.ini"); + cIniFile IniFile( m_WorldName + "/world.ini"); if( IniFile.ReadFile() ) { m_SpawnX = IniFile.GetValueF("SpawnPosition", "X", m_SpawnX ); @@ -181,7 +164,8 @@ cWorld::cWorld( const char* 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 ); - WorldGeneratorName = IniFile.GetValue("Generator", "GeneratorName", "Default"); + GeneratorName = IniFile.GetValue("Generator", "GeneratorName", "Default"); + StorageSchema = IniFile.GetValue("Storage", "Schema", "Default"); } else { @@ -198,11 +182,8 @@ cWorld::cWorld( const char* a_WorldName ) } LOGINFO("Seed: %i", m_WorldSeed ); - if( WorldGeneratorName.compare("Test") == 0 ) - m_WorldGenerator = new cWorldGenerator_Test(); - else // Default - m_WorldGenerator = new cWorldGenerator(); - + m_Storage.Start(this, StorageSchema); + m_Generator.Start(this, GeneratorName); cIniFile GenSettings("terrain.ini"); if( GenSettings.ReadFile() ) { @@ -238,16 +219,12 @@ cWorld::cWorld( const char* a_WorldName ) } m_ChunkMap = new cChunkMap(this ); - m_pState->pChunkGenerator = new cChunkGenerator( m_ChunkMap ); m_Time = 0; m_WorldTimeFraction = 0.f; m_WorldTime = 0; m_LastSave = 0; m_LastUnload = 0; - m_ClientHandleCriticalSection = new cCriticalSection(); - m_EntitiesCriticalSection = new cCriticalSection(); - m_ChunksCriticalSection = new cCriticalSection(); //Simulators: m_WaterSimulator = new cWaterSimulator( this ); @@ -380,7 +357,12 @@ void cWorld::SetWeather( int Weather ) } } -void cWorld::CastThunderbolt ( int X, int Y, int Z ) { + + + + +void cWorld::CastThunderbolt ( int X, int Y, int Z ) +{ cPacket_Thunderbolt ThunderboltPacket; ThunderboltPacket.m_xLBPos = X; ThunderboltPacket.m_yLBPos = Y; @@ -396,16 +378,18 @@ 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 = cClientHandle::VIEWDISTANCE; - LOG("Loading spawn area"); - for(int x = 0; x < ViewDist; x++) + 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++) { - for(int z = 0; z < ViewDist; z++) + for (int z = 0; z < ViewDist; z++) { - GetChunk( x + ChunkX-(ViewDist-1)/2, 0, z + ChunkZ-(ViewDist-1)/2 ); + GetChunk( x + ChunkX-(ViewDist - 1) / 2, 0, z + ChunkZ-(ViewDist - 1) / 2 ); // Queue the chunk in the generator / loader } - LOG("Loaded %0.2f", ((float)x / (float)ViewDist)*100 ); + LOG("Queued %0.2f %% of spawn area", ((float)x / (float)ViewDist) * 100 ); } + + // TODO: Wait for the generator to finish generating these chunks } @@ -415,188 +399,123 @@ void cWorld::InitializeSpawn() void cWorld::Tick(float a_Dt) { int randWeather = 0; - m_Time+=a_Dt/1000.f; + m_Time += a_Dt / 1000.f; CurrentTick++; bool bSendTime = false; - m_WorldTimeFraction+=a_Dt/1000.f; - while( m_WorldTimeFraction > 1.f ) + m_WorldTimeFraction += a_Dt / 1000.f; + while ( m_WorldTimeFraction > 1.f ) { - m_WorldTimeFraction-=1.f; - m_WorldTime+=20; - m_WorldTime %= 24000; // 24000 units in a day + m_WorldTimeFraction -= 1.f; + m_WorldTime += 20; bSendTime = true; } - if( bSendTime ) Broadcast( cPacket_TimeUpdate( (m_WorldTime) ) ); + m_WorldTime %= 24000; // 24000 units in a day + if ( bSendTime ) + { + Broadcast( cPacket_TimeUpdate( (m_WorldTime) ) ); + } - LockEntities(); - for( cWorld::EntityList::iterator itr = GetEntities().begin(); itr != GetEntities().end();) { - if( (*itr)->IsDestroyed() ) + cCSLock Lock(m_CSEntities); + for (cEntityList::iterator itr = m_AllEntities.begin(); itr != m_AllEntities.end();) { - LOG("Destroy that entity! %i", (*itr)->GetUniqueID() ); - cEntity* RemoveMe = *itr; - itr = m_pState->AllEntities.erase( itr ); - m_pState->RemoveEntityQueue.push_back( RemoveMe ); - continue; + if ((*itr)->IsDestroyed()) + { + LOG("Destroying entity #%i", (*itr)->GetUniqueID()); + cEntity * RemoveMe = *itr; + itr = m_AllEntities.erase( itr ); + m_RemoveEntityQueue.push_back( RemoveMe ); + continue; + } + (*itr)->Tick(a_Dt); + itr++; } - (*itr)->Tick(a_Dt); - itr++; } - UnlockEntities(); - - LockChunks(); - - int TimesSpreaded = 0; - while( !m_pState->SpreadQueue.empty() && TimesSpreaded < 50 ) // Spread a max of 50 times each tick, otherwise server will hang { - ptr_cChunk& Chunk = *m_pState->SpreadQueue.begin(); - //LOG("Spreading: %p", Chunk ); - Chunk->SpreadLight( Chunk->pGetSkyLight() ); - Chunk->SpreadLight( Chunk->pGetLight() ); - m_pState->SpreadQueue.remove( Chunk ); - TimesSpreaded++; - } - if( TimesSpreaded >= 50 ) - { - LOGWARN("Lots of lighting to do! At least %i chunks left!", m_pState->SpreadQueue.size() ); + 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++; + } } m_ChunkMap->Tick(a_Dt, m_TickRand); GetSimulatorManager()->Simulate(a_Dt); - UnlockChunks(); - TickWeather(a_Dt); // Asynchronously set blocks - FastSetBlockList FastSetBlockQueueCopy = m_pState->FastSetBlockQueue; - m_pState->FastSetBlockQueue.clear(); - for( FastSetBlockList::iterator itr = FastSetBlockQueueCopy.begin(); itr != FastSetBlockQueueCopy.end(); ++itr ) + FastSetBlockList FastSetBlockQueueCopy; + { + cCSLock Lock(m_CSFastSetBlock); + FastSetBlockQueueCopy = m_FastSetBlockQueue; + m_FastSetBlockQueue.clear(); + } + for ( FastSetBlockList::iterator itr = FastSetBlockQueueCopy.begin(); itr != FastSetBlockQueueCopy.end(); ++itr ) { sSetBlockData & SetBlockData = *itr; FastSetBlock( SetBlockData.x, SetBlockData.y, SetBlockData.z, SetBlockData.BlockID, SetBlockData.BlockMeta ); // If unable to set block, it's added to FastSetBlockQueue again } - if( FastSetBlockQueueCopy.size() != m_pState->FastSetBlockQueue.size() ) - LOG(" Before: %i, after %i" , FastSetBlockQueueCopy.size(), m_pState->FastSetBlockQueue.size() ); - if( m_Time - m_LastSave > 60*5 ) // Save each 5 minutes + if( m_Time - m_LastSave > 60 * 5 ) // Save each 5 minutes { SaveAllChunks(); } - if( m_Time - m_LastUnload > 10 ) // Unload each minute + if( m_Time - m_LastUnload > 10 ) // Unload every 10 seconds { UnloadUnusedChunks(); } - while( !m_pState->RemoveEntityQueue.empty() ) + // Delete entities queued for removal: + for (cEntityList::iterator itr = m_RemoveEntityQueue.begin(); itr != m_RemoveEntityQueue.end(); ++itr) { - RemoveEntity( *m_pState->RemoveEntityQueue.begin() ); - } - - if( m_bAnimals && ( m_Time - m_SpawnMonsterTime > m_SpawnMonsterRate ) ) // 10 seconds - { - m_SpawnMonsterTime = m_Time; - if( m_pState->Players.size() > 0 ) - { - cMonster *Monster = 0; - - //srand ( time(NULL) ); // Only seed random ONCE! Is already done in the cWorld constructor - int dayRand = m_TickRand.randInt() % 6; //added mob code - int nightRand = m_TickRand.randInt() % 10; //added mob code - - int RandomPlayerIdx = m_TickRand.randInt() & m_pState->Players.size(); - PlayerList::iterator itr = m_pState->Players.begin(); - for( int i = 1; i < RandomPlayerIdx; i++ ) - itr++; - - cPlayer* Player = *itr; - Vector3d SpawnPos = Player->GetPosition(); - 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 ); - - if(m_WorldTime >= 12000 + 1000) { - if (nightRand == 0) //random percent to spawn for night - Monster = new cSpider(); - else if (nightRand == 1) - Monster = new cZombie(); - else if (nightRand == 2) - Monster = new cEnderman(); - else if (nightRand == 3) - Monster = new cCreeper(); - else if (nightRand == 4) - Monster = new cCavespider(); - else if (nightRand == 5) - Monster = new cGhast(); - else if (nightRand == 6) - Monster = new cZombiepigman(); - else if (nightRand == 7) - Monster = new cSlime(); - else if (nightRand == 8) - Monster = new cSilverfish(); - else if (nightRand == 9) - Monster = new cSkeleton(); - //end random percent to spawn for night - } else { - if (dayRand == 0) //random percent to spawn for day - Monster = new cChicken(); - else if (dayRand == 1) - Monster = new cCow(); - else if (dayRand == 2) - Monster = new cPig(); - else if (dayRand == 3) - Monster = new cSheep(); - else if (dayRand == 4) - Monster = new cSquid(); - else if (dayRand == 5) - Monster = new cWolf(); - //end random percent to spawn for day - } - - if( Monster ) - { - Monster->Initialize( this ); - Monster->TeleportTo( SpawnPos.x, (double)(Height)+2, SpawnPos.z ); - Monster->SpawnOn( 0 ); - } - } + delete *itr; } + m_RemoveEntityQueue.clear(); + TickSpawnMobs(a_Dt); std::vector m_RSList_copy(m_RSList); - //copy(m_RSList.begin(), m_RSList.end(), m_RSList_copy.begin()); - m_RSList.erase(m_RSList.begin(),m_RSList.end()); - int tempX; // FIXME - Keep the scope in mind, these variables are not used in this scope at all, move them down into the for loop - int tempY; - int tempZ; - int state; + + m_RSList.clear(); std::vector::const_iterator cii; // FIXME - Please rename this variable, WTF is cii??? Use human readable variable names or common abbreviations (i, idx, itr, iter) for(cii=m_RSList_copy.begin(); cii!=m_RSList_copy.end();) { - tempX = *cii;cii++; - tempY = *cii;cii++; - tempZ = *cii;cii++; - state = *cii;cii++; + int tempX = *cii;cii++; + int tempY = *cii;cii++; + int tempZ = *cii;cii++; + int state = *cii;cii++; - //printf ("%i, %i, %i, %i\n",tempX,tempY,tempZ,state) ; - if ( (state == 11111) && ( (int)GetBlock( tempX, tempY, tempZ ) == E_BLOCK_REDSTONE_TORCH_OFF ) ) { + if ( (state == 11111) && ( (int)GetBlock( tempX, tempY, tempZ ) == E_BLOCK_REDSTONE_TORCH_OFF ) ) + { FastSetBlock( tempX, tempY, tempZ, E_BLOCK_REDSTONE_TORCH_ON, (int)GetBlockMeta( tempX, tempY, tempZ ) ); cRedstone Redstone(this); Redstone.ChangeRedstone( tempX, tempY, tempZ, true ); - } else if ( (state == 00000) && ( (int)GetBlock( tempX, tempY, tempZ ) == E_BLOCK_REDSTONE_TORCH_ON ) ) { + } + else if ( (state == 00000) && ( (int)GetBlock( tempX, tempY, tempZ ) == E_BLOCK_REDSTONE_TORCH_ON ) ) + { FastSetBlock( tempX, tempY, tempZ, E_BLOCK_REDSTONE_TORCH_OFF, (int)GetBlockMeta( tempX, tempY, tempZ ) ); cRedstone Redstone(this); Redstone.ChangeRedstone( tempX, tempY, tempZ, false ); } - } m_RSList_copy.erase(m_RSList_copy.begin(),m_RSList_copy.end()); - } @@ -605,7 +524,6 @@ void cWorld::Tick(float a_Dt) void cWorld::TickWeather(float a_Dt) { - ////////////////Weather/////////////////////// if ( GetWeather() == 0 ) // if sunny { if( CurrentTick % 19 == 0 ) //every 20 ticks random weather @@ -655,6 +573,90 @@ void cWorld::TickWeather(float a_Dt) +void cWorld::TickSpawnMobs(float a_Dt) +{ + if (!m_bAnimals || (m_Time - m_SpawnMonsterTime <= m_SpawnMonsterRate)) + { + return; + } + + m_SpawnMonsterTime = m_Time; + Vector3d SpawnPos; + { + cCSLock Lock(m_CSPlayers); + if ( m_Players.size() <= 0) + { + return; + } + int RandomPlayerIdx = m_TickRand.randInt() & m_Players.size(); + cPlayerList::iterator itr = m_Players.begin(); + for( int i = 1; i < RandomPlayerIdx; i++ ) + { + itr++; + } + SpawnPos = (*itr)->GetPosition(); + } + + cMonster * Monster = NULL; + int dayRand = m_TickRand.randInt() % 6; + 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 ); + + if (m_WorldTime >= 12000 + 1000) + { + if (nightRand == 0) //random percent to spawn for night + Monster = new cSpider(); + else if (nightRand == 1) + Monster = new cZombie(); + else if (nightRand == 2) + Monster = new cEnderman(); + else if (nightRand == 3) + Monster = new cCreeper(); + else if (nightRand == 4) + Monster = new cCavespider(); + else if (nightRand == 5) + Monster = new cGhast(); + else if (nightRand == 6) + Monster = new cZombiepigman(); + else if (nightRand == 7) + Monster = new cSlime(); + else if (nightRand == 8) + Monster = new cSilverfish(); + else if (nightRand == 9) + Monster = new cSkeleton(); + //end random percent to spawn for night + } + else + { + if (dayRand == 0) //random percent to spawn for day + Monster = new cChicken(); + else if (dayRand == 1) + Monster = new cCow(); + else if (dayRand == 2) + Monster = new cPig(); + else if (dayRand == 3) + Monster = new cSheep(); + else if (dayRand == 4) + Monster = new cSquid(); + else if (dayRand == 5) + Monster = new cWolf(); + //end random percent to spawn for day + } + + if( Monster ) + { + Monster->Initialize( this ); + Monster->TeleportTo( SpawnPos.x, (double)(Height) + 2, SpawnPos.z ); + Monster->SpawnOn(0); + } +} + + + + + void cWorld::GrowTree( int a_X, int a_Y, int a_Z ) { // new tree code, looks much better @@ -714,234 +716,234 @@ void cWorld::GrowTree( int a_X, int a_Y, int a_Z ) // end new tree code } -void cWorld::UnloadUnusedChunks() -{ - m_LastUnload = m_Time; - - LockChunks(); - LOGINFO("Unloading unused chunks"); - m_ChunkMap->UnloadUnusedChunks(); - UnlockChunks(); -} -cChunk* cWorld::GetChunkReliable( int a_X, int a_Y, int a_Z ) // TODO - FIXME - WARNING - This can cause a duplicate chunk to be generated!! -{ - cChunk* Chunk = GetChunkUnreliable( a_X, a_Y, a_Z ); - if( Chunk ) - { - return Chunk; - } - // Found nothing, create a chunk - Chunk = new cChunk( a_X, a_Y, a_Z, this ); - if(Chunk) - { - LOGWARN("Created new chunk! %i %i", a_X, a_Z); - LockChunks(); - m_ChunkMap->AddChunk( Chunk ); - UnlockChunks(); - Chunk->Initialize(); - return Chunk; - } - // This should never happen since it's reliable, but yeah - return 0; -} -cChunk* cWorld::GetChunk( int a_X, int a_Y, int a_Z ) +void cWorld::UnloadUnusedChunks() { - // Get chunk from memory - cChunk* Chunk = GetChunkUnreliable( a_X, a_Y, a_Z ); - if( Chunk ) return Chunk; - -#if 1 // Current thread chunk generation + m_LastUnload = m_Time; + m_ChunkMap->UnloadUnusedChunks(); +} - // Found nothing, create a chunk - Chunk = new cChunk( a_X, a_Y, a_Z, this ); - if(Chunk) - { - LOGWARN("Created new chunk! %i %i", a_X, a_Z); - LockChunks(); - m_ChunkMap->AddChunk( Chunk ); - UnlockChunks(); - Chunk->Initialize(); - return Chunk; - } - return 0; -#else // Async thread generation - // Generate new chunk asynchronously - m_pState->pChunkGenerator->GenerateChunk( a_X, a_Z ); - // Could not find chunk, it's being generated, so return 0 - return 0; -#endif -} -ptr_cChunk cWorld::GetChunkUnreliable( int a_X, int a_Y, int a_Z ) -{ - LockChunks(); - ptr_cChunk Chunk( m_ChunkMap->GetChunk( a_X, a_Y, a_Z ) ); - UnlockChunks(); - return Chunk; -} -cChunk* cWorld::GetChunkOfBlock( int a_X, int a_Y, int a_Z ) +cChunkPtr cWorld::GetChunkOfBlock( int a_X, int a_Y, int a_Z ) { int ChunkX, ChunkY, ChunkZ; AbsoluteToRelative( a_X, a_Y, a_Z, ChunkX, ChunkY, ChunkZ ); return GetChunk( ChunkX, ChunkY, ChunkZ ); } + + + + void cWorld::SetBlock( int a_X, int a_Y, int a_Z, char a_BlockType, char a_BlockMeta ) { int ChunkX, ChunkY, ChunkZ, X = a_X, Y = a_Y, Z = a_Z; AbsoluteToRelative( X, Y, Z, ChunkX, ChunkY, ChunkZ ); - cChunk* Chunk = GetChunk( ChunkX, ChunkY, ChunkZ ); - if( Chunk ) + cChunkPtr Chunk = GetChunk( ChunkX, ChunkY, ChunkZ ); + if ( Chunk->IsValid() ) { Chunk->SetBlock(X, Y, Z, a_BlockType, a_BlockMeta ); this->GetSimulatorManager()->WakeUp(a_X, a_Y, a_Z); } + // The chunk is not yet initialized, so it's probably far away from all players, no need to store this Meta change } + + + + void cWorld::FastSetBlock( int a_X, int a_Y, int a_Z, char a_BlockType, char a_BlockMeta ) { int ChunkX, ChunkY, ChunkZ, X = a_X, Y = a_Y, Z = a_Z; AbsoluteToRelative( X, Y, Z, ChunkX, ChunkY, ChunkZ ); - cChunk* Chunk = GetChunk( ChunkX, ChunkY, ChunkZ ); - if( Chunk ) + cChunkPtr Chunk = GetChunkNoGen( ChunkX, ChunkY, ChunkZ ); + if (Chunk->IsValid()) { Chunk->FastSetBlock(X, Y, Z, a_BlockType, a_BlockMeta ); return; } // Unable to set block right now, try again later - m_pState->FastSetBlockQueue.push_back( sSetBlockData( a_X, a_Y, a_Z, a_BlockType, a_BlockMeta ) ); + cCSLock Lock(m_CSFastSetBlock); + m_FastSetBlockQueue.push_back( sSetBlockData( a_X, a_Y, a_Z, a_BlockType, a_BlockMeta ) ); } -char cWorld::GetBlock( int a_X, int a_Y, int a_Z ) + + + + +char cWorld::GetBlock(int a_X, int a_Y, int a_Z) { int ChunkX, ChunkY, ChunkZ; AbsoluteToRelative( a_X, a_Y, a_Z, ChunkX, ChunkY, ChunkZ ); - cChunk* Chunk = GetChunk( ChunkX, ChunkY, ChunkZ ); - if( Chunk ) return Chunk->GetBlock(a_X, a_Y, a_Z); + cChunkPtr Chunk = GetChunk( ChunkX, ChunkY, ChunkZ ); + if ( Chunk->IsValid() ) + { + return Chunk->GetBlock(a_X, a_Y, a_Z); + } return 0; } + + + + char cWorld::GetBlockMeta( int a_X, int a_Y, int a_Z ) { int ChunkX, ChunkY, ChunkZ; AbsoluteToRelative( a_X, a_Y, a_Z, ChunkX, ChunkY, ChunkZ ); - cChunk* Chunk = GetChunk( ChunkX, ChunkY, ChunkZ ); - if( Chunk ) return Chunk->GetLight( Chunk->pGetMeta(), a_X, a_Y, a_Z ); + cChunkPtr Chunk = GetChunk( ChunkX, ChunkY, ChunkZ ); + if ( Chunk->IsValid() ) + { + return Chunk->GetLight( Chunk->pGetMeta(), a_X, a_Y, a_Z ); + } return 0; } + + + + void cWorld::SetBlockMeta( int a_X, int a_Y, int a_Z, char a_MetaData ) { int ChunkX, ChunkY, ChunkZ; AbsoluteToRelative( a_X, a_Y, a_Z, ChunkX, ChunkY, ChunkZ ); - cChunk* Chunk = GetChunk( ChunkX, ChunkY, ChunkZ ); - if( Chunk ) + cChunkPtr Chunk = GetChunk( ChunkX, ChunkY, ChunkZ ); + if ( Chunk->IsValid() ) { Chunk->SetLight( Chunk->pGetMeta(), a_X, a_Y, a_Z, a_MetaData ); - Chunk->SendBlockTo( a_X, a_Y, a_Z, 0 ); + Chunk->SendBlockTo( a_X, a_Y, a_Z, NULL ); } + // The chunk is not yet initialized, so it's probably far away from all players, no need to store this Meta change } + + + + bool cWorld::DigBlock( int a_X, int a_Y, int a_Z, cItem & a_PickupItem ) { int PosX = a_X, PosY = a_Y, PosZ = a_Z, ChunkX, ChunkY, ChunkZ; AbsoluteToRelative( PosX, PosY, PosZ, ChunkX, ChunkY, ChunkZ ); - cChunk* DestChunk = GetChunk( ChunkX, ChunkY, ChunkZ ); - if(DestChunk) + cChunkPtr DestChunk = GetChunk( ChunkX, ChunkY, ChunkZ ); + if (DestChunk->IsValid()) { DestChunk->SetBlock(PosX, PosY, PosZ, E_BLOCK_AIR, 0 ); GetSimulatorManager()->WakeUp(a_X, a_Y, a_Z); - if( !a_PickupItem.IsEmpty() ) + if ( !a_PickupItem.IsEmpty() ) { - cPickup* Pickup = new cPickup( a_X*32 + 16 + (int)fRadRand(16.f), a_Y*32 + 16 + (int)fRadRand(16.f), a_Z*32 + 16 + (int)fRadRand(16.f), a_PickupItem ); + cPickup * Pickup = new cPickup( a_X * 32 + 16 + (int)fRadRand(16.f), a_Y * 32 + 16 + (int)fRadRand(16.f), a_Z * 32 + 16 + (int)fRadRand(16.f), a_PickupItem ); Pickup->Initialize( this ); } } - return true; } -void cWorld::SendBlockTo( int a_X, int a_Y, int a_Z, cPlayer* a_Player ) + + + + +void cWorld::SendBlockTo( int a_X, int a_Y, int a_Z, cPlayer * a_Player ) { int ChunkX, ChunkY, ChunkZ; AbsoluteToRelative( a_X, a_Y, a_Z, ChunkX, ChunkY, ChunkZ ); - cChunk* Chunk = GetChunk( ChunkX, ChunkY, ChunkZ ); - Chunk->SendBlockTo( a_X, a_Y, a_Z, a_Player->GetClientHandle() ); + cChunkPtr Chunk = GetChunk( ChunkX, ChunkY, ChunkZ ); + if (Chunk->IsValid()) + { + Chunk->SendBlockTo( a_X, a_Y, a_Z, a_Player->GetClientHandle() ); + } } -cBlockEntity* cWorld::GetBlockEntity( int a_X, int a_Y, int a_Z ) + + + + +// TODO: This interface is dangerous! +cBlockEntity * cWorld::GetBlockEntity( int a_X, int a_Y, int a_Z ) { int PosX = a_X, PosY = a_Y, PosZ = a_Z, ChunkX, ChunkY, ChunkZ; AbsoluteToRelative( PosX, PosY, PosZ, ChunkX, ChunkY, ChunkZ ); - cChunk* Chunk = GetChunk( ChunkX, ChunkY, ChunkZ ); - if( !Chunk ) return 0; - - return Chunk->GetBlockEntity( a_X, a_Y, a_Z ); + cChunkPtr Chunk = GetChunk( ChunkX, ChunkY, ChunkZ ); + if (Chunk->IsValid()) + { + // TODO: return Chunk->GetBlockEntity( a_X, a_Y, a_Z ); + } + return NULL; } + + + + char 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 ); - cChunk* Chunk = GetChunk( ChunkX, ChunkY, ChunkZ ); - if( Chunk ) return Chunk->GetHeight( PosX, PosZ ); + cChunkPtr Chunk = GetChunk( ChunkX, ChunkY, ChunkZ ); + if ( Chunk->IsValid()) + { + return Chunk->GetHeight( PosX, PosZ ); + } return 0; } -const double & cWorld::GetSpawnY() + + + + +const double & cWorld::GetSpawnY(void) { m_SpawnY = (double)GetHeight( (int)m_SpawnX, (int)m_SpawnZ ) + 1.6f; // +1.6f eye height return m_SpawnY; } -void cWorld::Broadcast( const cPacket & a_Packet, cClientHandle* a_Exclude /* = 0 */ ) + + + +void cWorld::Broadcast( const cPacket & a_Packet, cClientHandle* a_Exclude) { - for( PlayerList::iterator itr = m_pState->Players.begin(); itr != m_pState->Players.end(); ++itr) + cCSLock Lock(m_CSPlayers); + for (cPlayerList::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) { - if( (*itr)->GetClientHandle() == a_Exclude || !(*itr)->GetClientHandle()->IsLoggedIn() ) continue; + if (((*itr)->GetClientHandle() == a_Exclude) || !(*itr)->GetClientHandle()->IsLoggedIn() ) + { + continue; + } (*itr)->GetClientHandle()->Send( a_Packet ); } } -std::string cWorld::GetDescription() -{ - return this->m_Description; -} -unsigned int cWorld::GetMaxPlayers() -{ - return this->m_MaxPlayers; -} + + void cWorld::SetMaxPlayers(int iMax) { - this->m_MaxPlayers = MAX_PLAYERS; + m_MaxPlayers = MAX_PLAYERS; if (iMax > 0 && iMax < MAX_PLAYERS) { - this->m_MaxPlayers = iMax; + m_MaxPlayers = iMax; } } @@ -951,8 +953,9 @@ void cWorld::SetMaxPlayers(int iMax) void cWorld::AddPlayer( cPlayer* a_Player ) { - m_pState->Players.remove( a_Player ); - m_pState->Players.push_back( a_Player ); + cCSLock Lock(m_CSPlayers); + m_Players.remove( a_Player ); // Make sure the player is registered only once + m_Players.push_back( a_Player ); } @@ -961,7 +964,26 @@ void cWorld::AddPlayer( cPlayer* a_Player ) void cWorld::RemovePlayer( cPlayer* a_Player ) { - m_pState->Players.remove( a_Player ); + cCSLock Lock(m_CSPlayers); + m_Players.remove( a_Player ); +} + + + + + +bool cWorld::ForEachPlayer(cPlayerListCallback * a_Callback) +{ + // Calls the callback for each player in the list + cCSLock Lock(m_CSPlayers); + for (cPlayerList::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) + { + if (a_Callback->Item(*itr)) + { + return false; + } + } // for itr - m_Players[] + return true; } @@ -970,11 +992,12 @@ void cWorld::RemovePlayer( cPlayer* a_Player ) void cWorld::GetAllPlayers( lua_State* L ) { - lua_createtable(L, m_pState->Players.size(), 0); + lua_createtable(L, m_Players.size(), 0); int newTable = lua_gettop(L); int index = 1; - PlayerList::const_iterator iter = m_pState->Players.begin(); - while(iter != m_pState->Players.end()) { + cPlayerList::const_iterator iter = m_Players.begin(); + while(iter != m_Players.end()) + { tolua_pushusertype( L, (*iter), "cPlayer" ); lua_rawseti(L, newTable, index); ++iter; @@ -986,6 +1009,7 @@ void cWorld::GetAllPlayers( lua_State* L ) +// TODO: This interface is dangerous! cPlayer* cWorld::GetPlayer( const char* a_PlayerName ) { cPlayer* BestMatch = 0; @@ -994,12 +1018,13 @@ cPlayer* cWorld::GetPlayer( const char* a_PlayerName ) bool bPerfectMatch = false; unsigned int NameLength = strlen( a_PlayerName ); - for( PlayerList::iterator itr = m_pState->Players.begin(); itr != m_pState->Players.end(); itr++ ) + cCSLock Lock(m_CSPlayers); + for (cPlayerList::iterator itr = m_Players.begin(); itr != m_Players.end(); itr++ ) { std::string Name = (*itr)->GetName(); if( NameLength > Name.length() ) continue; // Definitely not a match - for(unsigned int i = 0; i < NameLength; i++) + for (unsigned int i = 0; i < NameLength; i++) { char c1 = (char)toupper( a_PlayerName[i] ); char c2 = (char)toupper( Name[i] ); @@ -1029,65 +1054,91 @@ cPlayer* cWorld::GetPlayer( const char* a_PlayerName ) break; } } - if( NumMatches == 1 ) + if ( NumMatches == 1 ) + { return BestMatch; + } - // More than one matches, so it's undefined. Return 0 instead - return 0; + // More than one matches, so it's undefined. Return NULL instead + return NULL; } -cEntity* cWorld::GetEntity( int a_UniqueID ) +cPlayer * cWorld::FindClosestPlayer(const Vector3f & a_Pos, float a_SightLimit) { - for( EntityList::iterator itr = m_pState->AllEntities.begin(); itr != m_pState->AllEntities.end(); ++itr ) + cTracer LineOfSight(this); + + float ClosestDistance = a_SightLimit; + cPlayer* ClosestPlayer = NULL; + + cCSLock Lock(m_CSPlayers); + for (cPlayerList::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) { - if( (*itr)->GetUniqueID() == a_UniqueID ) - return *itr; + Vector3f Pos = (*itr)->GetPosition(); + float Distance = (Pos - a_Pos).Length(); + + if (Distance <= a_SightLimit) + { + if (!LineOfSight.Trace(a_Pos,(Pos - a_Pos),(int)(Pos - a_Pos).Length())) + { + if (Distance < ClosestDistance) + { + ClosestDistance = Distance; + ClosestPlayer = *itr; + } + } + } } - return 0; + return ClosestPlayer; } -// void cWorld::RemoveClient( cClientHandle* a_Client ) -// { -// m_pState->m_Clients.remove( a_Client ); -// if( a_Client ) -// { -// delete a_Client; -// a_Client = 0; -// } -// } +void cWorld::SendPlayerList(cPlayer * a_DestPlayer) +{ + // Sends the playerlist to a_DestPlayer + cCSLock Lock(m_CSPlayers); + for ( cPlayerList::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr) + { + if (((*itr)->GetClientHandle() != NULL) && !((*itr)->GetClientHandle()->IsDestroyed())) + { + cPacket_PlayerListItem PlayerListItem((*itr)->GetColor() + (*itr)->GetName(), true, (*itr)->GetClientHandle()->GetPing()); + a_DestPlayer->GetClientHandle()->Send( PlayerListItem ); + } + } +} -void cWorld::RemoveEntity( cEntity* a_Entity ) +// TODO: This interface is dangerous! +cEntity * cWorld::GetEntity( int a_UniqueID ) { - m_pState->RemoveEntityQueue.remove( a_Entity ); - if( a_Entity ) + cCSLock Lock(m_CSEntities); + for (cEntityList::iterator itr = m_AllEntities.begin(); itr != m_AllEntities.end(); ++itr ) { - delete a_Entity; - a_Entity = 0; + if( (*itr)->GetUniqueID() == a_UniqueID ) + { + return *itr; + } } + return NULL; } -bool cWorld::RemoveEntityFromChunk( cEntity & a_Entity, cChunk* a_CalledFrom /* = 0 */ ) +void cWorld::RemoveEntityFromChunk(cEntity * a_Entity) { - LockChunks(); - bool retVal = m_ChunkMap->RemoveEntityFromChunk( a_Entity, a_CalledFrom ); - UnlockChunks(); - return retVal; + cChunkPtr Chunk = GetChunkOfBlock((int)(a_Entity->GetPosX()), (int)(a_Entity->GetPosY()), (int)(a_Entity->GetPosZ())); + Chunk->RemoveEntity(a_Entity); } @@ -1098,9 +1149,7 @@ void cWorld::SaveAllChunks() { LOG("Saving all chunks..."); m_LastSave = m_Time; - LockChunks(); m_ChunkMap->SaveAllChunks(); - UnlockChunks(); LOG("Done saving chunks"); } @@ -1108,79 +1157,21 @@ void cWorld::SaveAllChunks() -void cWorld::LockClientHandle() -{ - m_ClientHandleCriticalSection->Lock(); -} - - - - - -void cWorld::UnlockClientHandle() -{ - m_ClientHandleCriticalSection->Unlock(); -} - - - - - -void cWorld::LockEntities() -{ - m_EntitiesCriticalSection->Lock(); -} - - - - - -void cWorld::UnlockEntities() -{ - m_EntitiesCriticalSection->Unlock(); -} - - - - - -void cWorld::LockChunks() -{ - m_ChunksCriticalSection->Lock(); -} - - - - - -void cWorld::UnlockChunks() +void cWorld::ReSpreadLighting( const cChunkPtr & a_Chunk ) { - m_ChunksCriticalSection->Unlock(); + cCSLock Lock(m_CSLighting); + m_SpreadQueue.remove( a_Chunk ); + m_SpreadQueue.push_back( a_Chunk ); } -void cWorld::ReSpreadLighting( const ptr_cChunk& a_Chunk ) +void cWorld::RemoveSpread( const cChunkPtr & a_Chunk ) { - LockChunks(); - m_pState->SpreadQueue.remove( a_Chunk ); - m_pState->SpreadQueue.push_back( a_Chunk ); - //#define STRINGIZE(x) #x - //a_Chunk->AddReference( __FILE__ ": " STRINGIZE(__LINE__) ); - UnlockChunks(); -} - - - - - -void cWorld::RemoveSpread( const ptr_cChunk& a_Chunk ) -{ - LockChunks(); - m_pState->SpreadQueue.remove( a_Chunk ); - UnlockChunks(); + cCSLock Lock(m_CSLighting); + m_SpreadQueue.remove( a_Chunk ); } @@ -1192,38 +1183,21 @@ void cWorld::RemoveSpread( const ptr_cChunk& a_Chunk ) /************************************************************************/ // void cWorld::AddClient( cClientHandle* a_Client ) // { -// m_pState->m_Clients.push_back( a_Client ); +// m_m_Clients.push_back( a_Client ); // } // cWorld::ClientList & cWorld::GetClients() // { -// return m_pState->m_Clients; +// return m_m_Clients; // } -cWorld::EntityList & cWorld::GetEntities() -{ - return m_pState->AllEntities; -} - - - - - void cWorld::AddEntity( cEntity* a_Entity ) { - m_pState->AllEntities.push_back( a_Entity ); -} - - - - - -cWorld::PlayerList & cWorld::GetAllPlayers() -{ - return m_pState->Players; + cCSLock Lock(m_CSEntities); + m_AllEntities.push_back( a_Entity ); } @@ -1232,38 +1206,17 @@ cWorld::PlayerList & cWorld::GetAllPlayers() unsigned int cWorld::GetNumPlayers() { - return m_pState->Players.size(); -} - - - - - -void cWorld::AddToRemoveEntityQueue( cEntity & a_Entity ) -{ - m_pState->AllEntities.remove( &a_Entity); - m_pState->RemoveEntityQueue.push_back( &a_Entity ); -} - - - - - -const char* cWorld::GetName() -{ - return m_pState->WorldName.c_str(); + cCSLock Lock(m_CSPlayers); + return m_Players.size(); } -int cWorld::GetNumChunks(void) +int cWorld::GetNumChunks(void) const { - LockChunks(); - int NumChunks = m_ChunkMap->GetNumChunks(); - UnlockChunks(); - return NumChunks; + return m_ChunkMap->GetNumChunks(); } -- cgit v1.2.3