9f9a79068e
git-svn-id: http://mc-server.googlecode.com/svn/trunk@1290 0a769ca7-a7f5-676a-18bf-c427514a06d6
334 lines
6.8 KiB
C++
334 lines
6.8 KiB
C++
|
|
// BiomeCache.cpp
|
|
|
|
// Implements the cBiomeCache class representing a biome source that caches data from the underlying biome source
|
|
|
|
#include "Globals.h"
|
|
#include "BiomeCache.h"
|
|
#include "Timer.h"
|
|
|
|
|
|
|
|
|
|
|
|
static int GetNumCores(void)
|
|
{
|
|
// Get number of cores by querying the system process affinity mask
|
|
DWORD Affinity, ProcAffinity;
|
|
GetProcessAffinityMask(GetCurrentProcess(), &ProcAffinity, &Affinity);
|
|
int NumCores = 0;
|
|
while (Affinity > 0)
|
|
{
|
|
if ((Affinity & 1) == 1)
|
|
{
|
|
NumCores++;
|
|
}
|
|
Affinity >>= 1;
|
|
} // while (Affinity > 0)
|
|
return NumCores;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
cBiomeCache::cBiomeCache(void) :
|
|
m_Source(NULL),
|
|
m_BaseX(-100000),
|
|
m_BaseZ(-100000),
|
|
m_Available(NULL),
|
|
m_IsTerminatingThreads(false)
|
|
{
|
|
int NumThreads = GetNumCores();
|
|
NumThreads--; // One core should be left for the system to run on ;)
|
|
for (int i = NumThreads; i > 0; i--)
|
|
{
|
|
cThread * Thread = new cThread(*this);
|
|
m_Threads.push_back(Thread);
|
|
Thread->Start();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
cBiomeCache::~cBiomeCache()
|
|
{
|
|
m_IsTerminatingThreads = true;
|
|
for (cThreads::iterator itr = m_Threads.begin(), end = m_Threads.end(); itr != end; ++itr)
|
|
{
|
|
m_evtQueued.Set();
|
|
}
|
|
for (cThreads::iterator itr = m_Threads.begin(), end = m_Threads.end(); itr != end; ++itr)
|
|
{
|
|
delete *itr;
|
|
}
|
|
m_Threads.clear();
|
|
|
|
SetSource(NULL);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
cBiomeSource::eAvailability cBiomeCache::GetBiome(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_Biomes)
|
|
{
|
|
if (m_Source == NULL)
|
|
{
|
|
return baNever;
|
|
}
|
|
|
|
// Look up using the cache:
|
|
int x = a_ChunkX - m_BaseX;
|
|
int z = a_ChunkZ - m_BaseZ;
|
|
if ((x < 0) || (x >= m_Width) || (z < 0) || (z >= m_Height))
|
|
{
|
|
// Outside the cached region
|
|
return baNever;
|
|
}
|
|
|
|
cCSLock Lock(m_CS);
|
|
cItem * Item = m_Available[x + m_Width * z];
|
|
if (Item == NULL)
|
|
{
|
|
// Item hasn't been processed yet
|
|
return baLater;
|
|
}
|
|
if (Item->m_IsValid)
|
|
{
|
|
memcpy(a_Biomes, Item->m_Biomes, sizeof(a_Biomes));
|
|
return baNow;
|
|
}
|
|
|
|
// Item has been processed, but the underlying source refused to give the data to us
|
|
return baNever;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cBiomeCache::HintViewArea(int a_MinChunkX, int a_MaxChunkX, int a_MinChunkZ, int a_MaxChunkZ)
|
|
{
|
|
cTimer Timer("Cache: HintViewArea");
|
|
|
|
if (
|
|
(a_MinChunkX == m_BaseX) &&
|
|
(a_MaxChunkX == m_BaseX + m_Width - 1) &&
|
|
(a_MinChunkZ == m_BaseZ) &&
|
|
(a_MaxChunkZ == m_BaseZ + m_Height - 1)
|
|
)
|
|
{
|
|
// The same set of parameters, bail out
|
|
return;
|
|
}
|
|
|
|
if (m_Source != NULL)
|
|
{
|
|
m_Source->HintViewArea(a_MinChunkX, a_MaxChunkX, a_MinChunkZ, a_MaxChunkZ);
|
|
}
|
|
|
|
int NewWidth = a_MaxChunkX - a_MinChunkX + 1;
|
|
int NewHeight = a_MaxChunkZ - a_MinChunkZ + 1;
|
|
|
|
// Make a new empty cache table:
|
|
pItem * NewAvailable = new pItem[NewWidth * NewHeight];
|
|
for (int i = NewWidth * NewHeight - 1; i >= 0; --i)
|
|
{
|
|
NewAvailable[i] = NULL;
|
|
}
|
|
|
|
// Move the common contents of the old table into the new table:
|
|
cCSLock Lock(m_CS);
|
|
for (int z = 0; z < NewHeight; z++)
|
|
{
|
|
int OldZ = z + a_MinChunkZ - m_BaseZ;
|
|
if ((OldZ < 0) || (OldZ >= m_Height))
|
|
{
|
|
continue;
|
|
}
|
|
for (int x = 0; x < NewWidth; x++)
|
|
{
|
|
int OldX = x + a_MinChunkX - m_BaseX;
|
|
if ((OldX < 0) || (OldX >= m_Width))
|
|
{
|
|
continue;
|
|
}
|
|
NewAvailable[x + NewWidth * z] = m_Available[OldX + m_Width * OldZ];
|
|
m_Available[OldX + m_Width * OldZ] = NULL;
|
|
} // for x
|
|
} // for z
|
|
|
|
// All items that aren't common go into the pool:
|
|
for (int idx = 0, z = 0; z < m_Height; z++)
|
|
{
|
|
for (int x = 0; x < m_Width; ++x, ++idx)
|
|
{
|
|
if (m_Available[idx] != NULL)
|
|
{
|
|
m_Pool.push_back(m_Available[idx]);
|
|
m_Available[idx] = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Replace the cache table:
|
|
delete m_Available;
|
|
m_Available = NewAvailable;
|
|
m_Width = NewWidth;
|
|
m_Height = NewHeight;
|
|
m_BaseX = a_MinChunkX;
|
|
m_BaseZ = a_MinChunkZ;
|
|
|
|
// Remove all items outside the coords:
|
|
FilterOutItems(m_Queue, a_MinChunkX, a_MaxChunkX, a_MinChunkZ, a_MaxChunkZ);
|
|
|
|
// Queue all items from inside the coords into m_Queue:
|
|
for (int z = 0; z < NewHeight; z++)
|
|
{
|
|
for (int x = 0; x < NewWidth; x++)
|
|
{
|
|
if (m_Available[x + m_Width * z] != NULL)
|
|
{
|
|
// Already calculated, skip
|
|
continue;
|
|
}
|
|
|
|
if (m_Pool.empty())
|
|
{
|
|
m_Pool.push_back(new cItem(x + a_MinChunkX, z + a_MinChunkZ));
|
|
}
|
|
ASSERT(!m_Pool.empty());
|
|
m_Pool.back()->m_ChunkX = x + a_MinChunkX;
|
|
m_Pool.back()->m_ChunkZ = z + a_MinChunkZ;
|
|
m_Queue.push_back(m_Pool.back());
|
|
m_Pool.pop_back();
|
|
m_evtQueued.Set();
|
|
} // for x
|
|
} // for z
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cBiomeCache::SetSource(cBiomeSource * a_Source)
|
|
{
|
|
// TODO: Stop all threads, so that they don't use the source anymore!
|
|
|
|
delete m_Source;
|
|
m_Source = a_Source;
|
|
|
|
// Invalidate cache contents:
|
|
cCSLock Lock(m_CS);
|
|
m_BaseX = -10000;
|
|
m_BaseZ = -10000;
|
|
m_Pool.splice(m_Pool.end(), m_Queue);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cBiomeCache::FilterOutItems(cItems & a_Items, int a_MinChunkX, int a_MaxChunkX, int a_MinChunkZ, int a_MaxChunkZ)
|
|
{
|
|
for (cItems::iterator itr = a_Items.begin(); itr != a_Items.end();)
|
|
{
|
|
if (
|
|
((*itr)->m_ChunkX < a_MinChunkX) ||
|
|
((*itr)->m_ChunkX > a_MaxChunkX) ||
|
|
((*itr)->m_ChunkX < a_MinChunkX) ||
|
|
((*itr)->m_ChunkX > a_MaxChunkX)
|
|
)
|
|
{
|
|
m_Pool.push_back(*itr);
|
|
itr = a_Items.erase(itr);
|
|
}
|
|
else
|
|
{
|
|
++itr;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cBiomeCache::thrProcessQueueItem(void)
|
|
{
|
|
cItem * Item = NULL;
|
|
{
|
|
cCSLock Lock(m_CS);
|
|
if (m_Queue.empty())
|
|
{
|
|
cCSUnlock Unlock(Lock);
|
|
m_evtQueued.Wait();
|
|
}
|
|
if (m_IsTerminatingThreads || m_Queue.empty())
|
|
{
|
|
// We've been woken up only to die / spurious wakeup
|
|
return;
|
|
}
|
|
Item = m_Queue.back();
|
|
m_Queue.pop_back();
|
|
}
|
|
|
|
// Process the item:
|
|
Item->m_IsValid = (m_Source->GetBiome(Item->m_ChunkX, Item->m_ChunkZ, Item->m_Biomes) == baNow);
|
|
|
|
// Store result:
|
|
cCSLock Lock(m_CS);
|
|
int x = Item->m_ChunkX - m_BaseX;
|
|
int z = Item->m_ChunkZ - m_BaseZ;
|
|
if ((x < 0) || (x >= m_Width) || (z < 0) || (z >= m_Height))
|
|
{
|
|
// The cache rectangle has changed under our fingers, drop this chunk
|
|
return;
|
|
}
|
|
m_Available[x + m_Width * z] = Item;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// cBiomeCache::cItem:
|
|
|
|
cBiomeCache::cItem::cItem(int a_ChunkX, int a_ChunkZ) :
|
|
m_ChunkX(a_ChunkX),
|
|
m_ChunkZ(a_ChunkZ)
|
|
{
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// cBiomeCache::cThread:
|
|
|
|
cBiomeCache::cThread::cThread(cBiomeCache & a_Parent) :
|
|
super("Biome cache thread"),
|
|
m_Parent(a_Parent)
|
|
{
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void cBiomeCache::cThread::Execute(void)
|
|
{
|
|
while (!m_ShouldTerminate && !m_Parent.m_IsTerminatingThreads)
|
|
{
|
|
m_Parent.thrProcessQueueItem();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|