1
0

Separated chunk generator from world / plugin interfaces.

The generator now only takes care of servicing synchronous "GetChunk(X, Y)" and "GetBiomes(X, Y)" requests.
This commit is contained in:
Mattes D 2019-09-01 09:30:00 +02:00
parent f021e2fe22
commit a2ffa432b3
27 changed files with 783 additions and 689 deletions

View File

@ -727,9 +727,9 @@ static int tolua_cWorld_PrepareChunk(lua_State * tolua_S)
{
public:
// cChunkCoordCallback override:
virtual void Call(int a_CBChunkX, int a_CBChunkZ, bool a_IsSuccess) override
virtual void Call(cChunkCoords a_Coords, bool a_IsSuccess) override
{
m_LuaCallback.Call(a_CBChunkX, a_CBChunkZ, a_IsSuccess);
m_LuaCallback.Call(a_Coords.m_ChunkX, a_Coords.m_ChunkZ, a_IsSuccess);
}
cLuaState::cOptionalCallback m_LuaCallback;

View File

@ -27,6 +27,7 @@ SET (SRCS
ChatColor.cpp
Chunk.cpp
ChunkData.cpp
ChunkGeneratorThread.cpp
ChunkMap.cpp
ChunkSender.cpp
ChunkStay.cpp
@ -100,6 +101,7 @@ SET (HDRS
ChunkData.h
ChunkDataCallback.h
ChunkDef.h
ChunkGeneratorThread.h
ChunkMap.h
ChunkSender.h
ChunkStay.h

View File

@ -49,23 +49,6 @@
////////////////////////////////////////////////////////////////////////////////
// sSetBlock:
sSetBlock::sSetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta):
m_RelX(a_BlockX),
m_RelY(a_BlockY),
m_RelZ(a_BlockZ),
m_BlockType(a_BlockType),
m_BlockMeta(a_BlockMeta)
{
cChunkDef::AbsoluteToRelative(m_RelX, m_RelY, m_RelZ, m_ChunkX, m_ChunkZ);
}
////////////////////////////////////////////////////////////////////////////////
// cChunk:
@ -276,7 +259,7 @@ void cChunk::MarkLoadFailed(void)
// If the chunk is marked as needed, generate it:
if (m_ShouldGenerateIfLoadFailed)
{
m_World->GetGenerator().QueueGenerateChunk(m_PosX, m_PosZ, false);
m_World->GetGenerator().QueueGenerateChunk({m_PosX, m_PosZ}, false);
}
else
{

View File

@ -65,6 +65,12 @@ public:
{
return ((m_ChunkX == a_Other.m_ChunkX) && (m_ChunkZ == a_Other.m_ChunkZ));
}
/** Returns a string that describes the chunk coords, suitable for logging. */
AString ToString() const
{
return Printf("[%d, %d]", m_ChunkX, m_ChunkZ);
}
} ;
@ -445,7 +451,15 @@ struct sSetBlock
BLOCKTYPE m_BlockType;
NIBBLETYPE m_BlockMeta;
sSetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta);
sSetBlock(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta):
m_RelX(a_BlockX),
m_RelY(a_BlockY),
m_RelZ(a_BlockZ),
m_BlockType(a_BlockType),
m_BlockMeta(a_BlockMeta)
{
cChunkDef::AbsoluteToRelative(m_RelX, m_RelY, m_RelZ, m_ChunkX, m_ChunkZ);
}
sSetBlock(int a_ChunkX, int a_ChunkZ, int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta) :
m_RelX(a_RelX), m_RelY(a_RelY), m_RelZ(a_RelZ),
@ -525,7 +539,7 @@ public:
virtual ~cChunkCoordCallback() {}
/** Called with the chunk's coords, and an optional operation status flag for operations that support it. */
virtual void Call(int a_ChunkX, int a_ChunkZ, bool a_IsSuccess) = 0;
virtual void Call(cChunkCoords a_Coords, bool a_IsSuccess) = 0;
} ;

View File

@ -0,0 +1,264 @@
#include "Globals.h"
#include "ChunkGeneratorThread.h"
#include "Generating/ChunkGenerator.h"
#include "Generating/ChunkDesc.h"
/** If the generation queue size exceeds this number, a warning will be output */
const size_t QUEUE_WARNING_LIMIT = 1000;
/** If the generation queue size exceeds this number, chunks with no clients will be skipped */
const size_t QUEUE_SKIP_LIMIT = 500;
cChunkGeneratorThread::cChunkGeneratorThread(void) :
Super("cChunkGeneratorThread"),
m_Generator(nullptr),
m_PluginInterface(nullptr),
m_ChunkSink(nullptr)
{
}
cChunkGeneratorThread::~cChunkGeneratorThread()
{
Stop();
}
bool cChunkGeneratorThread::Initialize(cPluginInterface & a_PluginInterface, cChunkSink & a_ChunkSink, cIniFile & a_IniFile)
{
m_PluginInterface = &a_PluginInterface;
m_ChunkSink = &a_ChunkSink;
m_Generator = cChunkGenerator::CreateFromIniFile(a_IniFile);
if (m_Generator == nullptr)
{
LOGERROR("Generator could not start, aborting the server");
return false;
}
return true;
}
void cChunkGeneratorThread::Stop(void)
{
m_ShouldTerminate = true;
m_Event.Set();
m_evtRemoved.Set(); // Wake up anybody waiting for empty queue
Super::Stop();
m_Generator.reset();
}
void cChunkGeneratorThread::QueueGenerateChunk(
cChunkCoords a_Coords,
bool a_ForceRegeneration,
cChunkCoordCallback * a_Callback
)
{
ASSERT(m_ChunkSink->IsChunkQueued(a_Coords));
{
cCSLock Lock(m_CS);
// Add to queue, issue a warning if too many:
if (m_Queue.size() >= QUEUE_WARNING_LIMIT)
{
LOGWARN("WARNING: Adding chunk %s to generation queue; Queue is too big! (%zu)", a_Coords.ToString().c_str(), m_Queue.size());
}
m_Queue.push_back(QueueItem{a_Coords, a_ForceRegeneration, a_Callback});
}
m_Event.Set();
}
void cChunkGeneratorThread::GenerateBiomes(cChunkCoords a_Coords, cChunkDef::BiomeMap & a_BiomeMap)
{
if (m_Generator != nullptr)
{
m_Generator->GenerateBiomes(a_Coords.m_ChunkX, a_Coords.m_ChunkZ, a_BiomeMap);
}
}
void cChunkGeneratorThread::WaitForQueueEmpty(void)
{
cCSLock Lock(m_CS);
while (!m_ShouldTerminate && !m_Queue.empty())
{
cCSUnlock Unlock(Lock);
m_evtRemoved.Wait();
}
}
int cChunkGeneratorThread::GetQueueLength(void) const
{
cCSLock Lock(m_CS);
return static_cast<int>(m_Queue.size());
}
int cChunkGeneratorThread::GetSeed() const
{
return m_Generator->GetSeed();
}
EMCSBiome cChunkGeneratorThread::GetBiomeAt(int a_BlockX, int a_BlockZ)
{
ASSERT(m_Generator != nullptr);
return m_Generator->GetBiomeAt(a_BlockX, a_BlockZ);
}
void cChunkGeneratorThread::Execute(void)
{
// To be able to display performance information, the generator counts the chunks generated.
// When the queue gets empty, the count is reset, so that waiting for the queue is not counted into the total time.
int NumChunksGenerated = 0; // Number of chunks generated since the queue was last empty
clock_t GenerationStart = clock(); // Clock tick when the queue started to fill
clock_t LastReportTick = clock(); // Clock tick of the last report made (so that performance isn't reported too often)
while (!m_ShouldTerminate)
{
cCSLock Lock(m_CS);
while (m_Queue.empty())
{
if ((NumChunksGenerated > 16) && (clock() - LastReportTick > CLOCKS_PER_SEC))
{
/* LOG("Chunk generator performance: %.2f ch / sec (%d ch total)",
static_cast<double>(NumChunksGenerated) * CLOCKS_PER_SEC/ (clock() - GenerationStart),
NumChunksGenerated
); */
}
cCSUnlock Unlock(Lock);
m_Event.Wait();
if (m_ShouldTerminate)
{
return;
}
NumChunksGenerated = 0;
GenerationStart = clock();
LastReportTick = clock();
}
if (m_Queue.empty())
{
// Sometimes the queue remains empty
// If so, we can't do any front() operations on it!
continue;
}
auto item = m_Queue.front(); // Get next chunk from the queue
bool SkipEnabled = (m_Queue.size() > QUEUE_SKIP_LIMIT);
m_Queue.erase(m_Queue.begin()); // Remove the item from the queue
Lock.Unlock(); // Unlock ASAP
m_evtRemoved.Set();
// Display perf info once in a while:
if ((NumChunksGenerated > 512) && (clock() - LastReportTick > 2 * CLOCKS_PER_SEC))
{
LOG("Chunk generator performance: %.2f ch / sec (%d ch total)",
static_cast<double>(NumChunksGenerated) * CLOCKS_PER_SEC / (clock() - GenerationStart),
NumChunksGenerated
);
LastReportTick = clock();
}
// Skip the chunk if it's already generated and regeneration is not forced. Report as success:
if (!item.m_ForceRegeneration && m_ChunkSink->IsChunkValid(item.m_Coords))
{
LOGD("Chunk %s already generated, skipping generation", item.m_Coords.ToString().c_str());
if (item.m_Callback != nullptr)
{
item.m_Callback->Call(item.m_Coords, true);
}
continue;
}
// Skip the chunk if the generator is overloaded:
if (SkipEnabled && !m_ChunkSink->HasChunkAnyClients(item.m_Coords))
{
LOGWARNING("Chunk generator overloaded, skipping chunk %s", item.m_Coords.ToString().c_str());
if (item.m_Callback != nullptr)
{
item.m_Callback->Call(item.m_Coords, false);
}
continue;
}
// Generate the chunk:
DoGenerate(item.m_Coords);
if (item.m_Callback != nullptr)
{
item.m_Callback->Call(item.m_Coords, true);
}
NumChunksGenerated++;
} // while (!bStop)
}
void cChunkGeneratorThread::DoGenerate(cChunkCoords a_Coords)
{
ASSERT(m_PluginInterface != nullptr);
ASSERT(m_ChunkSink != nullptr);
cChunkDesc ChunkDesc(a_Coords);
m_PluginInterface->CallHookChunkGenerating(ChunkDesc);
m_Generator->Generate(a_Coords.m_ChunkX, a_Coords.m_ChunkZ, ChunkDesc);
m_PluginInterface->CallHookChunkGenerated(ChunkDesc);
#ifdef _DEBUG
// Verify that the generator has produced valid data:
ChunkDesc.VerifyHeightmap();
#endif
m_ChunkSink->OnChunkGenerated(ChunkDesc);
}

159
src/ChunkGeneratorThread.h Normal file
View File

@ -0,0 +1,159 @@
#pragma once
#include "OSSupport/IsThread.h"
// fwd:
class cIniFile;
class cChunkDesc;
class cChunkGenerator;
/** Takes requests for generating chunks and processes them in a separate thread one by one.
The requests are not added to the queue if there is already a request with the same coords.
Before generating, the thread checks if the chunk hasn't been already generated.
It is theoretically possible to have multiple generator threads by having multiple instances of this object,
but then it MAY happen that the chunk is generated twice.
If the generator queue is overloaded, the generator skips chunks with no clients in them. */
class cChunkGeneratorThread :
public cIsThread
{
using Super = cIsThread;
public:
/** The interface through which the plugins are called for their OnChunkGenerating / OnChunkGenerated hooks. */
class cPluginInterface
{
public:
// Force a virtual destructor
virtual ~cPluginInterface() {}
/** Called when the chunk is about to be generated.
The generator may be partly or fully overriden by the implementation. */
virtual void CallHookChunkGenerating(cChunkDesc & a_ChunkDesc) = 0;
/** Called after the chunk is generated, before it is handed to the chunk sink.
a_ChunkDesc contains the generated chunk data. Implementation may modify this data. */
virtual void CallHookChunkGenerated(cChunkDesc & a_ChunkDesc) = 0;
} ;
/** The interface through which the generated chunks are handed to the cWorld or whoever created us. */
class cChunkSink
{
public:
// Force a virtual destructor
virtual ~cChunkSink() {}
/** Called after the chunk has been generated
The interface may store the chunk, send it over network, whatever.
The chunk is not expected to be modified, but the generator will survive if the implementation
changes the data within. All changes are ignored, though. */
virtual void OnChunkGenerated(cChunkDesc & a_ChunkDesc) = 0;
/** Called just before the chunk generation is started,
to verify that it hasn't been generated in the meantime.
If this callback returns true, the chunk is not generated. */
virtual bool IsChunkValid(cChunkCoords a_Coords) = 0;
/** Called when the generator is overloaded to skip chunks that are no longer needed.
If this callback returns false, the chunk is not generated. */
virtual bool HasChunkAnyClients(cChunkCoords a_Coords) = 0;
/** Called to check whether the specified chunk is in the queued state.
Currently used only in Debug-mode asserts. */
virtual bool IsChunkQueued(cChunkCoords a_Coords) = 0;
} ;
cChunkGeneratorThread (void);
virtual ~cChunkGeneratorThread() override;
/** Read settings from the ini file and initialize in preperation for being started. */
bool Initialize(cPluginInterface & a_PluginInterface, cChunkSink & a_ChunkSink, cIniFile & a_IniFile);
void Stop(void);
/** Queues the chunk for generation
If a-ForceGenerate is set, the chunk is regenerated even if the data is already present in the chunksink.
a_Callback is called after the chunk is generated. If the chunk was already present, the callback is still called, even if not regenerating.
It is legal to set the callback to nullptr, no callback is called then.
If the generator becomes overloaded and skips this chunk, the callback is still called. */
void QueueGenerateChunk(cChunkCoords a_Coords, bool a_ForceRegeneration, cChunkCoordCallback * a_Callback = nullptr);
/** Generates the biomes for the specified chunk (directly, not in a separate thread). Used by the world loader if biomes failed loading. */
void GenerateBiomes(cChunkCoords a_Coords, cChunkDef::BiomeMap & a_BiomeMap);
void WaitForQueueEmpty();
int GetQueueLength() const;
int GetSeed() const;
/** Returns the biome at the specified coords. Used by ChunkMap if an invalid chunk is queried for biome */
EMCSBiome GetBiomeAt(int a_BlockX, int a_BlockZ);
private:
struct QueueItem
{
/** The chunk coords */
cChunkCoords m_Coords;
/** Force the regeneration of an already existing chunk */
bool m_ForceRegeneration;
/** Callback to call after generating. */
cChunkCoordCallback * m_Callback;
QueueItem(cChunkCoords a_Coords, bool a_ForceRegeneration, cChunkCoordCallback * a_Callback):
m_Coords(a_Coords),
m_ForceRegeneration(a_ForceRegeneration),
m_Callback(a_Callback)
{
}
};
using Queue = std::list<QueueItem>;
/** CS protecting access to the queue. */
mutable cCriticalSection m_CS;
/** Queue of the chunks to be generated. Protected against multithreaded access by m_CS. */
Queue m_Queue;
/** Set when an item is added to the queue or the thread should terminate. */
cEvent m_Event;
/** Set when an item is removed from the queue. */
cEvent m_evtRemoved;
/** The actual chunk generator engine used. */
std::unique_ptr<cChunkGenerator> m_Generator;
/** The plugin interface that may modify the generated chunks */
cPluginInterface * m_PluginInterface;
/** The destination where the generated chunks are sent */
cChunkSink * m_ChunkSink;
// cIsThread override:
virtual void Execute(void) override;
/** Generates the specified chunk and sets it into the chunksink. */
void DoGenerate(cChunkCoords a_Coords);
};

View File

@ -1798,7 +1798,7 @@ void cChunkMap::PrepareChunk(int a_ChunkX, int a_ChunkZ, std::unique_ptr<cChunkC
// The chunk is present and lit, just call the callback, report as success:
if (a_Callback != nullptr)
{
a_Callback->Call(a_ChunkX, a_ChunkZ, true);
a_Callback->Call({a_ChunkX, a_ChunkZ}, true);
}
}
@ -1831,34 +1831,34 @@ bool cChunkMap::GenerateChunk(int a_ChunkX, int a_ChunkZ, cChunkCoordCallback *
}
// cChunkCoordCallback override:
virtual void Call(int a_CBChunkX, int a_CBChunkZ, bool a_CBIsSuccess) override
virtual void Call(cChunkCoords a_Coords, bool a_CBIsSuccess) override
{
// If success is reported, the chunk is already valid, no need to do anything else:
if (a_CBIsSuccess)
{
if (m_Callback != nullptr)
{
m_Callback->Call(a_CBChunkX, a_CBChunkZ, true);
m_Callback->Call(a_Coords, true);
}
return;
}
// The chunk failed to load, generate it:
cCSLock CBLock(m_ChunkMap.m_CSChunks);
cChunkPtr CBChunk = m_ChunkMap.GetChunkNoLoad(a_CBChunkX, a_CBChunkZ);
cChunkPtr CBChunk = m_ChunkMap.GetChunkNoLoad(a_Coords.m_ChunkX, a_Coords.m_ChunkZ);
if (CBChunk == nullptr)
{
// An error occurred, but we promised to call the callback, so call it even when there's no real chunk data:
if (m_Callback != nullptr)
{
m_Callback->Call(a_CBChunkX, a_CBChunkZ, false);
m_Callback->Call(a_Coords, false);
}
return;
}
CBChunk->SetPresence(cChunk::cpQueued);
m_World.GetGenerator().QueueGenerateChunk(a_CBChunkX, a_CBChunkZ, false, m_Callback);
m_World.GetGenerator().QueueGenerateChunk(a_Coords, false, m_Callback);
}
protected:
@ -1873,7 +1873,7 @@ bool cChunkMap::GenerateChunk(int a_ChunkX, int a_ChunkZ, cChunkCoordCallback *
// The chunk is valid, just call the callback:
if (a_Callback != nullptr)
{
a_Callback->Call(a_ChunkX, a_ChunkZ, true);
a_Callback->Call({a_ChunkX, a_ChunkZ}, true);
}
return true;
}

View File

@ -27,11 +27,11 @@
class cNotifyChunkSender :
public cChunkCoordCallback
{
virtual void Call(int a_ChunkX, int a_ChunkZ, bool a_IsSuccess) override
virtual void Call(cChunkCoords a_Coords, bool a_IsSuccess) override
{
cChunkSender & ChunkSender = m_ChunkSender;
m_World.DoWithChunk(
a_ChunkX, a_ChunkZ,
a_Coords.m_ChunkX, a_Coords.m_ChunkZ,
[&ChunkSender] (cChunk & a_Chunk) -> bool
{
ChunkSender.QueueSendChunkTo(a_Chunk.GetPosX(), a_Chunk.GetPosZ(), cChunkSender::E_CHUNK_PRIORITY_MIDHIGH, a_Chunk.GetAllClients());

View File

@ -13,9 +13,8 @@
cChunkDesc::cChunkDesc(int a_ChunkX, int a_ChunkZ) :
m_ChunkX(a_ChunkX),
m_ChunkZ(a_ChunkZ),
cChunkDesc::cChunkDesc(cChunkCoords a_Coords) :
m_Coords(a_Coords),
m_bUseDefaultBiomes(true),
m_bUseDefaultHeight(true),
m_bUseDefaultComposition(true),
@ -43,10 +42,9 @@ cChunkDesc::~cChunkDesc()
void cChunkDesc::SetChunkCoords(int a_ChunkX, int a_ChunkZ)
void cChunkDesc::SetChunkCoords(cChunkCoords a_Coords)
{
m_ChunkX = a_ChunkX;
m_ChunkZ = a_ChunkZ;
m_Coords = a_Coords;
}
@ -369,9 +367,9 @@ void cChunkDesc::ReadBlockArea(cBlockArea & a_Dest, int a_MinRelX, int a_MaxRelX
int SizeY = a_MaxRelY - a_MinRelY;
int SizeZ = a_MaxRelZ - a_MinRelZ;
a_Dest.Clear();
a_Dest.m_Origin.x = m_ChunkX * cChunkDef::Width + a_MinRelX;
a_Dest.m_Origin.x = m_Coords.m_ChunkX * cChunkDef::Width + a_MinRelX;
a_Dest.m_Origin.y = a_MinRelY;
a_Dest.m_Origin.z = m_ChunkZ * cChunkDef::Width + a_MinRelZ;
a_Dest.m_Origin.z = m_Coords.m_ChunkZ * cChunkDef::Width + a_MinRelZ;
a_Dest.SetSize(SizeX, SizeY, SizeZ, cBlockArea::baTypes | cBlockArea::baMetas);
for (int y = 0; y < SizeY; y++)
@ -593,8 +591,8 @@ cBlockEntity * cChunkDesc::GetBlockEntity(int a_RelX, int a_RelY, int a_RelZ)
}
}
int AbsX = a_RelX + m_ChunkX * cChunkDef::Width;
int AbsZ = a_RelZ + m_ChunkZ * cChunkDef::Width;
int AbsX = a_RelX + m_Coords.m_ChunkX * cChunkDef::Width;
int AbsZ = a_RelZ + m_Coords.m_ChunkZ * cChunkDef::Width;
// The block entity is not created yet, try to create it and add to list:
cBlockEntity * be = cBlockEntity::CreateByBlockType(GetBlockType(a_RelX, a_RelY, a_RelZ), GetBlockMeta(a_RelX, a_RelY, a_RelZ), AbsX, a_RelY, AbsZ);

View File

@ -39,15 +39,21 @@ public:
typedef NIBBLETYPE BlockNibbleBytes[cChunkDef::NumBlocks];
cChunkDesc(int a_ChunkX, int a_ChunkZ);
cChunkDesc(cChunkCoords a_Coords);
~cChunkDesc();
void SetChunkCoords(int a_ChunkX, int a_ChunkZ);
void SetChunkCoords(cChunkCoords a_Coords);
// tolua_begin
int GetChunkX(void) const { return m_ChunkX; }
int GetChunkZ(void) const { return m_ChunkZ; }
int GetChunkX() const { return m_Coords.m_ChunkX; } // Prefer GetChunkCoords() instead
int GetChunkZ() const { return m_Coords.m_ChunkZ; } // Prefer GetChunkCoords() instead
// tolua_end
cChunkCoords GetChunkCoords() const { return m_Coords; }
// tolua_begin
void FillBlocks(BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta);
void SetBlockTypeMeta(int a_RelX, int a_RelY, int a_RelZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta);
@ -225,8 +231,7 @@ public:
#endif // _DEBUG
private:
int m_ChunkX;
int m_ChunkZ;
cChunkCoords m_Coords;
cChunkDef::BiomeMap m_BiomeMap;
cBlockArea m_BlockArea;

View File

@ -12,46 +12,8 @@
/** If the generation queue size exceeds this number, a warning will be output */
const unsigned int QUEUE_WARNING_LIMIT = 1000;
/** If the generation queue size exceeds this number, chunks with no clients will be skipped */
const unsigned int QUEUE_SKIP_LIMIT = 500;
////////////////////////////////////////////////////////////////////////////////
// cChunkGenerator:
cChunkGenerator::cChunkGenerator(void) :
super("cChunkGenerator"),
m_Seed(0), // Will be overwritten by the actual generator
m_Generator(nullptr),
m_PluginInterface(nullptr),
m_ChunkSink(nullptr)
void cChunkGenerator::Initialize(cIniFile & a_IniFile)
{
}
cChunkGenerator::~cChunkGenerator()
{
Stop();
}
bool cChunkGenerator::Initialize(cPluginInterface & a_PluginInterface, cChunkSink & a_ChunkSink, cIniFile & a_IniFile)
{
m_PluginInterface = &a_PluginInterface;
m_ChunkSink = &a_ChunkSink;
// Get the seed; create a new one and log it if not found in the INI file:
if (a_IniFile.HasValue("Seed", "Seed"))
{
@ -63,12 +25,20 @@ bool cChunkGenerator::Initialize(cPluginInterface & a_PluginInterface, cChunkSin
LOGINFO("Chosen a new random seed for world: %d", m_Seed);
a_IniFile.SetValueI("Seed", "Seed", m_Seed);
}
}
std::unique_ptr<cChunkGenerator> cChunkGenerator::CreateFromIniFile(cIniFile & a_IniFile)
{
// Get the generator engine based on the INI file settings:
std::unique_ptr<cChunkGenerator> res;
AString GeneratorName = a_IniFile.GetValueSet("Generator", "Generator", "Composable");
if (NoCaseCompare(GeneratorName, "Noise3D") == 0)
{
m_Generator = new cNoise3DGenerator(*this);
res.reset(new cNoise3DGenerator());
}
else
{
@ -76,90 +46,17 @@ bool cChunkGenerator::Initialize(cPluginInterface & a_PluginInterface, cChunkSin
{
LOGWARN("[Generator]::Generator value \"%s\" not recognized, using \"Composable\".", GeneratorName.c_str());
}
m_Generator = new cComposableGenerator(*this);
res.reset(new cComposableGenerator());
}
if (m_Generator == nullptr)
if (res == nullptr)
{
LOGERROR("Generator could not start, aborting the server");
return false;
return nullptr;
}
m_Generator->Initialize(a_IniFile);
return true;
}
void cChunkGenerator::Stop(void)
{
m_ShouldTerminate = true;
m_Event.Set();
m_evtRemoved.Set(); // Wake up anybody waiting for empty queue
super::Stop();
delete m_Generator;
m_Generator = nullptr;
}
void cChunkGenerator::QueueGenerateChunk(int a_ChunkX, int a_ChunkZ, bool a_ForceGenerate, cChunkCoordCallback * a_Callback)
{
ASSERT(m_ChunkSink->IsChunkQueued(a_ChunkX, a_ChunkZ));
{
cCSLock Lock(m_CS);
// Add to queue, issue a warning if too many:
if (m_Queue.size() >= QUEUE_WARNING_LIMIT)
{
LOGWARN("WARNING: Adding chunk [%i, %i] to generation queue; Queue is too big! (%zu)", a_ChunkX, a_ChunkZ, m_Queue.size());
}
m_Queue.push_back(cQueueItem{a_ChunkX, a_ChunkZ, a_ForceGenerate, a_Callback});
}
m_Event.Set();
}
void cChunkGenerator::GenerateBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap)
{
if (m_Generator != nullptr)
{
m_Generator->GenerateBiomes(a_ChunkX, a_ChunkZ, a_BiomeMap);
}
}
void cChunkGenerator::WaitForQueueEmpty(void)
{
cCSLock Lock(m_CS);
while (!m_ShouldTerminate && !m_Queue.empty())
{
cCSUnlock Unlock(Lock);
m_evtRemoved.Wait();
}
}
int cChunkGenerator::GetQueueLength(void)
{
cCSLock Lock(m_CS);
return static_cast<int>(m_Queue.size());
res->Initialize(a_IniFile);
return res;
}
@ -167,166 +64,6 @@ int cChunkGenerator::GetQueueLength(void)
EMCSBiome cChunkGenerator::GetBiomeAt(int a_BlockX, int a_BlockZ)
{
ASSERT(m_Generator != nullptr);
return m_Generator->GetBiomeAt(a_BlockX, a_BlockZ);
}
BLOCKTYPE cChunkGenerator::GetIniBlock(cIniFile & a_IniFile, const AString & a_SectionName, const AString & a_ValueName, const AString & a_Default)
{
AString BlockType = a_IniFile.GetValueSet(a_SectionName, a_ValueName, a_Default);
int Block = BlockStringToType(BlockType);
if (Block < 0)
{
LOGWARN("[%s].%s Could not parse block value \"%s\". Using default: \"%s\".", a_SectionName.c_str(), a_ValueName.c_str(), BlockType.c_str(), a_Default.c_str());
return static_cast<BLOCKTYPE>(BlockStringToType(a_Default));
}
return static_cast<BLOCKTYPE>(Block);
}
void cChunkGenerator::Execute(void)
{
// To be able to display performance information, the generator counts the chunks generated.
// When the queue gets empty, the count is reset, so that waiting for the queue is not counted into the total time.
int NumChunksGenerated = 0; // Number of chunks generated since the queue was last empty
clock_t GenerationStart = clock(); // Clock tick when the queue started to fill
clock_t LastReportTick = clock(); // Clock tick of the last report made (so that performance isn't reported too often)
while (!m_ShouldTerminate)
{
cCSLock Lock(m_CS);
while (m_Queue.empty())
{
if ((NumChunksGenerated > 16) && (clock() - LastReportTick > CLOCKS_PER_SEC))
{
/* LOG("Chunk generator performance: %.2f ch / sec (%d ch total)",
static_cast<double>(NumChunksGenerated) * CLOCKS_PER_SEC/ (clock() - GenerationStart),
NumChunksGenerated
); */
}
cCSUnlock Unlock(Lock);
m_Event.Wait();
if (m_ShouldTerminate)
{
return;
}
NumChunksGenerated = 0;
GenerationStart = clock();
LastReportTick = clock();
}
if (m_Queue.empty())
{
// Sometimes the queue remains empty
// If so, we can't do any front() operations on it!
continue;
}
cQueueItem item = m_Queue.front(); // Get next chunk from the queue
bool SkipEnabled = (m_Queue.size() > QUEUE_SKIP_LIMIT);
m_Queue.erase(m_Queue.begin()); // Remove the item from the queue
Lock.Unlock(); // Unlock ASAP
m_evtRemoved.Set();
// Display perf info once in a while:
if ((NumChunksGenerated > 512) && (clock() - LastReportTick > 2 * CLOCKS_PER_SEC))
{
LOG("Chunk generator performance: %.2f ch / sec (%d ch total)",
static_cast<double>(NumChunksGenerated) * CLOCKS_PER_SEC / (clock() - GenerationStart),
NumChunksGenerated
);
LastReportTick = clock();
}
// Skip the chunk if it's already generated and regeneration is not forced. Report as success:
if (!item.m_ForceGenerate && m_ChunkSink->IsChunkValid(item.m_ChunkX, item.m_ChunkZ))
{
LOGD("Chunk [%d, %d] already generated, skipping generation", item.m_ChunkX, item.m_ChunkZ);
if (item.m_Callback != nullptr)
{
item.m_Callback->Call(item.m_ChunkX, item.m_ChunkZ, true);
}
continue;
}
// Skip the chunk if the generator is overloaded:
if (SkipEnabled && !m_ChunkSink->HasChunkAnyClients(item.m_ChunkX, item.m_ChunkZ))
{
LOGWARNING("Chunk generator overloaded, skipping chunk [%d, %d]", item.m_ChunkX, item.m_ChunkZ);
if (item.m_Callback != nullptr)
{
item.m_Callback->Call(item.m_ChunkX, item.m_ChunkZ, false);
}
continue;
}
// Generate the chunk:
// LOGD("Generating chunk [%d, %d]", item.m_ChunkX, item.m_ChunkZ);
DoGenerate(item.m_ChunkX, item.m_ChunkZ);
if (item.m_Callback != nullptr)
{
item.m_Callback->Call(item.m_ChunkX, item.m_ChunkZ, true);
}
NumChunksGenerated++;
} // while (!bStop)
}
void cChunkGenerator::DoGenerate(int a_ChunkX, int a_ChunkZ)
{
ASSERT(m_PluginInterface != nullptr);
ASSERT(m_ChunkSink != nullptr);
cChunkDesc ChunkDesc(a_ChunkX, a_ChunkZ);
m_PluginInterface->CallHookChunkGenerating(ChunkDesc);
m_Generator->DoGenerate(a_ChunkX, a_ChunkZ, ChunkDesc);
m_PluginInterface->CallHookChunkGenerated(ChunkDesc);
#ifdef _DEBUG
// Verify that the generator has produced valid data:
ChunkDesc.VerifyHeightmap();
#endif
m_ChunkSink->OnChunkGenerated(ChunkDesc);
}
////////////////////////////////////////////////////////////////////////////////
// cChunkGenerator::cGenerator:
cChunkGenerator::cGenerator::cGenerator(cChunkGenerator & a_ChunkGenerator) :
m_ChunkGenerator(a_ChunkGenerator)
{
}
void cChunkGenerator::cGenerator::Initialize(cIniFile & a_IniFile)
{
UNUSED(a_IniFile);
}
EMCSBiome cChunkGenerator::cGenerator::GetBiomeAt(int a_BlockX, int a_BlockZ)
{
cChunkDef::BiomeMap Biomes;
int Y = 0;

View File

@ -1,25 +1,5 @@
// ChunkGenerator.h
// Interfaces to the cChunkGenerator class representing the thread that generates chunks
/*
The object takes requests for generating chunks and processes them in a separate thread one by one.
The requests are not added to the queue if there is already a request with the same coords
Before generating, the thread checks if the chunk hasn't been already generated.
It is theoretically possible to have multiple generator threads by having multiple instances of this object,
but then it MAY happen that the chunk is generated twice.
If the generator queue is overloaded, the generator skips chunks with no clients in them
*/
#pragma once
#include "../OSSupport/IsThread.h"
@ -32,158 +12,44 @@ class cChunkDesc;
class cChunkGenerator :
public cIsThread
/** The interface that all chunk generators must implement to provide the generated chunks.
Also a static factory that creates the descendants based on INI file settings.
The cChunkGeneratorThread uses this interface to generate chunks for a single world.
Ths calls to generate chunks are synchronous - they don't return until the chunk is fully generated. */
class cChunkGenerator
{
typedef cIsThread super;
public:
/** The interface that a class has to implement to become a generator */
class cGenerator
{
public:
cGenerator(cChunkGenerator & a_ChunkGenerator);
virtual ~cGenerator() {} // Force a virtual destructor
virtual ~cChunkGenerator() {} // Force a virtual destructor
/** Called to initialize the generator on server startup. */
virtual void Initialize(cIniFile & a_IniFile);
/** Called to initialize the generator on server startup.
Descendants should call Super::Initialize() before initializing themselves. */
virtual void Initialize(cIniFile & a_IniFile);
/** Generates the biomes for the specified chunk (directly, not in a separate thread). Used by the world loader if biomes failed loading. */
virtual void GenerateBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) = 0;
/** Generates the biomes for the specified chunk.
Used by the world loader if biomes failed loading. */
virtual void GenerateBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) = 0;
/** Returns the biome at the specified coords. Used by ChunkMap if an invalid chunk is queried for biome. Default implementation uses GenerateBiomes(). */
virtual EMCSBiome GetBiomeAt(int a_BlockX, int a_BlockZ);
/** Returns the biome at the specified coords.
Used by ChunkMap if an invalid chunk is queried for biome.
The default implementation uses GenerateBiomes(). */
virtual EMCSBiome GetBiomeAt(int a_BlockX, int a_BlockZ);
/** Called in a separate thread to do the actual chunk generation. Generator should generate into a_ChunkDesc. */
virtual void DoGenerate(int a_ChunkX, int a_ChunkZ, cChunkDesc & a_ChunkDesc) = 0;
protected:
cChunkGenerator & m_ChunkGenerator;
} ;
/** The interface through which the plugins are called for their OnChunkGenerating / OnChunkGenerated hooks. */
class cPluginInterface
{
public:
// Force a virtual destructor
virtual ~cPluginInterface() {}
/** Called when the chunk is about to be generated.
The generator may be partly or fully overriden by the implementation. */
virtual void CallHookChunkGenerating(cChunkDesc & a_ChunkDesc) = 0;
/** Called after the chunk is generated, before it is handed to the chunk sink.
a_ChunkDesc contains the generated chunk data. Implementation may modify this data. */
virtual void CallHookChunkGenerated(cChunkDesc & a_ChunkDesc) = 0;
} ;
/** The interface through which the generated chunks are handed to the cWorld or whoever created us. */
class cChunkSink
{
public:
// Force a virtual destructor
virtual ~cChunkSink() {}
/** Called after the chunk has been generated
The interface may store the chunk, send it over network, whatever.
The chunk is not expected to be modified, but the generator will survive if the implementation
changes the data within. All changes are ignored, though. */
virtual void OnChunkGenerated(cChunkDesc & a_ChunkDesc) = 0;
/** Called just before the chunk generation is started,
to verify that it hasn't been generated in the meantime.
If this callback returns true, the chunk is not generated. */
virtual bool IsChunkValid(int a_ChunkX, int a_ChunkZ) = 0;
/** Called when the generator is overloaded to skip chunks that are no longer needed.
If this callback returns false, the chunk is not generated. */
virtual bool HasChunkAnyClients(int a_ChunkX, int a_ChunkZ) = 0;
/** Called to check whether the specified chunk is in the queued state.
Currently used only in Debug-mode asserts. */
virtual bool IsChunkQueued(int a_ChunkX, int a_ChunkZ) = 0;
} ;
cChunkGenerator (void);
virtual ~cChunkGenerator() override;
/** Read settings from the ini file and initialize in preperation for being started. */
bool Initialize(cPluginInterface & a_PluginInterface, cChunkSink & a_ChunkSink, cIniFile & a_IniFile);
void Stop(void);
/** Queues the chunk for generation
If a-ForceGenerate is set, the chunk is regenerated even if the data is already present in the chunksink.
a_Callback is called after the chunk is generated. If the chunk was already present, the callback is still called, even if not regenerating.
It is legal to set the callback to nullptr, no callback is called then.
If the generator becomes overloaded and skips this chunk, the callback is still called. */
void QueueGenerateChunk(int a_ChunkX, int a_ChunkZ, bool a_ForceGenerate, cChunkCoordCallback * a_Callback = nullptr);
/** Generates the biomes for the specified chunk (directly, not in a separate thread). Used by the world loader if biomes failed loading. */
void GenerateBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap);
void WaitForQueueEmpty(void);
int GetQueueLength(void);
/** Does the actual chunk generation.
Descendants need to override this and generate into a_ChunkDesc. */
virtual void Generate(int a_ChunkX, int a_ChunkZ, cChunkDesc & a_ChunkDesc) = 0;
/** Returns the seed that was read from the INI file. */
int GetSeed(void) const { return m_Seed; }
/** Returns the biome at the specified coords. Used by ChunkMap if an invalid chunk is queried for biome */
EMCSBiome GetBiomeAt(int a_BlockX, int a_BlockZ);
/** Reads a block type from the ini file; returns the blocktype on success, emits a warning and returns a_Default's representation on failure. */
static BLOCKTYPE GetIniBlock(cIniFile & a_IniFile, const AString & a_SectionName, const AString & a_ValueName, const AString & a_Default);
private:
struct cQueueItem
{
/** The chunk coords */
int m_ChunkX, m_ChunkZ;
/** Force the regeneration of an already existing chunk */
bool m_ForceGenerate;
/** Callback to call after generating. */
cChunkCoordCallback * m_Callback;
};
typedef std::list<cQueueItem> cGenQueue;
/** Creates and initializes the entire generator based on the settings in the INI file.
Initializes the generator, so that it can be used immediately after this call returns. */
static std::unique_ptr<cChunkGenerator> CreateFromIniFile(cIniFile & a_IniFile);
/** Seed used for the generator. */
protected:
/** The main seed, read from the INI file, used for the entire generator. */
int m_Seed;
/** CS protecting access to the queue. */
cCriticalSection m_CS;
/** Queue of the chunks to be generated. Protected against multithreaded access by m_CS. */
cGenQueue m_Queue;
/** Set when an item is added to the queue or the thread should terminate. */
cEvent m_Event;
/** Set when an item is removed from the queue. */
cEvent m_evtRemoved;
/** The actual generator engine used to generate chunks. */
cGenerator * m_Generator;
/** The plugin interface that may modify the generated chunks */
cPluginInterface * m_PluginInterface;
/** The destination where the generated chunks are sent */
cChunkSink * m_ChunkSink;
// cIsThread override:
virtual void Execute(void) override;
/** Generates the specified chunk and sets it into the chunksink. */
void DoGenerate(int a_ChunkX, int a_ChunkZ);
};

View File

@ -6,9 +6,7 @@
#include "Globals.h"
#include "ComposableGenerator.h"
#include "../World.h"
#include "../IniFile.h"
#include "../Root.h"
// Individual composed algorithms:
#include "BioGen.h"
@ -110,8 +108,7 @@ cTerrainCompositionGenPtr cTerrainCompositionGen::CreateCompositionGen(cIniFile
////////////////////////////////////////////////////////////////////////////////
// cComposableGenerator:
cComposableGenerator::cComposableGenerator(cChunkGenerator & a_ChunkGenerator) :
super(a_ChunkGenerator),
cComposableGenerator::cComposableGenerator():
m_BiomeGen(),
m_ShapeGen(),
m_CompositionGen()
@ -124,7 +121,7 @@ cComposableGenerator::cComposableGenerator(cChunkGenerator & a_ChunkGenerator) :
void cComposableGenerator::Initialize(cIniFile & a_IniFile)
{
super::Initialize(a_IniFile);
Super::Initialize(a_IniFile);
InitBiomeGen(a_IniFile);
InitShapeGen(a_IniFile);
@ -148,7 +145,7 @@ void cComposableGenerator::GenerateBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef:
void cComposableGenerator::DoGenerate(int a_ChunkX, int a_ChunkZ, cChunkDesc & a_ChunkDesc)
void cComposableGenerator::Generate(int a_ChunkX, int a_ChunkZ, cChunkDesc & a_ChunkDesc)
{
if (a_ChunkDesc.IsUsingDefaultBiomes())
{
@ -195,7 +192,7 @@ void cComposableGenerator::DoGenerate(int a_ChunkX, int a_ChunkZ, cChunkDesc & a
void cComposableGenerator::InitBiomeGen(cIniFile & a_IniFile)
{
bool CacheOffByDefault = false;
m_BiomeGen = cBiomeGen::CreateBiomeGen(a_IniFile, m_ChunkGenerator.GetSeed(), CacheOffByDefault);
m_BiomeGen = cBiomeGen::CreateBiomeGen(a_IniFile, m_Seed, CacheOffByDefault);
// Add a cache, if requested:
// The default is 16 * 128 caches, which is 2 MiB of RAM. Reasonable, for the amount of work this is saving.
@ -231,7 +228,7 @@ void cComposableGenerator::InitBiomeGen(cIniFile & a_IniFile)
void cComposableGenerator::InitShapeGen(cIniFile & a_IniFile)
{
bool CacheOffByDefault = false;
m_ShapeGen = cTerrainShapeGen::CreateShapeGen(a_IniFile, m_BiomeGen, m_ChunkGenerator.GetSeed(), CacheOffByDefault);
m_ShapeGen = cTerrainShapeGen::CreateShapeGen(a_IniFile, m_BiomeGen, m_Seed, CacheOffByDefault);
/*
// TODO
@ -258,7 +255,7 @@ void cComposableGenerator::InitShapeGen(cIniFile & a_IniFile)
void cComposableGenerator::InitCompositionGen(cIniFile & a_IniFile)
{
m_CompositionGen = cTerrainCompositionGen::CreateCompositionGen(a_IniFile, m_BiomeGen, m_ShapeGen, m_ChunkGenerator.GetSeed());
m_CompositionGen = cTerrainCompositionGen::CreateCompositionGen(a_IniFile, m_BiomeGen, m_ShapeGen, m_Seed);
// Add a cache over the composition generator:
// Even a cache of size 1 is useful due to the CompositedHeiGen cache after us doing re-composition on its misses
@ -279,7 +276,6 @@ void cComposableGenerator::InitCompositionGen(cIniFile & a_IniFile)
void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile)
{
int Seed = m_ChunkGenerator.GetSeed();
eDimension Dimension = StringToDimension(a_IniFile.GetValue("General", "Dimension", "Overworld"));
auto seaLevel = a_IniFile.GetValueI("Generator", "SeaLevel");
@ -298,7 +294,7 @@ void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile)
// Finishers, alpha-sorted:
if (NoCaseCompare(finisher, "Animals") == 0)
{
m_FinishGens.push_back(cFinishGenPtr(new cFinishGenPassiveMobs(Seed, a_IniFile, Dimension)));
m_FinishGens.push_back(cFinishGenPtr(new cFinishGenPassiveMobs(m_Seed, a_IniFile, Dimension)));
}
else if (NoCaseCompare(finisher, "BottomLava") == 0)
{
@ -327,15 +323,15 @@ void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile)
AllowedBlocks.push_back(E_BLOCK_HARDENED_CLAY);
AllowedBlocks.push_back(E_BLOCK_STAINED_CLAY);
m_FinishGens.push_back(cFinishGenPtr(new cFinishGenSingleTopBlock(Seed, E_BLOCK_DEAD_BUSH, AllowedBiomes, 2, AllowedBlocks)));
m_FinishGens.push_back(cFinishGenPtr(new cFinishGenSingleTopBlock(m_Seed, E_BLOCK_DEAD_BUSH, AllowedBiomes, 2, AllowedBlocks)));
}
else if (NoCaseCompare(finisher, "DirectOverhangs") == 0)
{
m_FinishGens.push_back(cFinishGenPtr(new cStructGenDirectOverhangs(Seed)));
m_FinishGens.push_back(cFinishGenPtr(new cStructGenDirectOverhangs(m_Seed)));
}
else if (NoCaseCompare(finisher, "DirtPockets") == 0)
{
auto gen = std::make_shared<cFinishGenOrePockets>(Seed + 1, cFinishGenOrePockets::DefaultNaturalPatches());
auto gen = std::make_shared<cFinishGenOrePockets>(m_Seed + 1, cFinishGenOrePockets::DefaultNaturalPatches());
if (gen->Initialize(a_IniFile, "DirtPockets"))
{
m_FinishGens.push_back(gen);
@ -343,12 +339,12 @@ void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile)
}
else if (NoCaseCompare(finisher, "DistortedMembraneOverhangs") == 0)
{
m_FinishGens.push_back(cFinishGenPtr(new cStructGenDistortedMembraneOverhangs(Seed)));
m_FinishGens.push_back(cFinishGenPtr(new cStructGenDistortedMembraneOverhangs(m_Seed)));
}
else if (NoCaseCompare(finisher, "DualRidgeCaves") == 0)
{
float Threshold = static_cast<float>(a_IniFile.GetValueSetF("Generator", "DualRidgeCavesThreshold", 0.3));
m_FinishGens.push_back(cFinishGenPtr(new cStructGenDualRidgeCaves(Seed, Threshold)));
m_FinishGens.push_back(cFinishGenPtr(new cStructGenDualRidgeCaves(m_Seed, Threshold)));
}
else if (NoCaseCompare(finisher, "DungeonRooms") == 0)
{
@ -356,11 +352,11 @@ void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile)
int MaxSize = a_IniFile.GetValueSetI("Generator", "DungeonRoomsMaxSize", 7);
int MinSize = a_IniFile.GetValueSetI("Generator", "DungeonRoomsMinSize", 5);
AString HeightDistrib = a_IniFile.GetValueSet ("Generator", "DungeonRoomsHeightDistrib", "0, 0; 10, 10; 11, 500; 40, 500; 60, 40; 90, 1");
m_FinishGens.push_back(cFinishGenPtr(new cDungeonRoomsFinisher(m_ShapeGen, Seed, GridSize, MaxSize, MinSize, HeightDistrib)));
m_FinishGens.push_back(cFinishGenPtr(new cDungeonRoomsFinisher(m_ShapeGen, m_Seed, GridSize, MaxSize, MinSize, HeightDistrib)));
}
else if (NoCaseCompare(finisher, "GlowStone") == 0)
{
m_FinishGens.push_back(cFinishGenPtr(new cFinishGenGlowStone(Seed)));
m_FinishGens.push_back(cFinishGenPtr(new cFinishGenGlowStone(m_Seed)));
}
else if (NoCaseCompare(finisher, "Ice") == 0)
{
@ -369,11 +365,11 @@ void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile)
else if (NoCaseCompare(finisher, "LavaLakes") == 0)
{
int Probability = a_IniFile.GetValueSetI("Generator", "LavaLakesProbability", 10);
m_FinishGens.push_back(cFinishGenPtr(new cStructGenLakes(Seed * 5 + 16873, E_BLOCK_STATIONARY_LAVA, m_ShapeGen, Probability)));
m_FinishGens.push_back(cFinishGenPtr(new cStructGenLakes(m_Seed * 5 + 16873, E_BLOCK_STATIONARY_LAVA, m_ShapeGen, Probability)));
}
else if (NoCaseCompare(finisher, "LavaSprings") == 0)
{
m_FinishGens.push_back(cFinishGenPtr(new cFinishGenFluidSprings(Seed, E_BLOCK_LAVA, a_IniFile, Dimension)));
m_FinishGens.push_back(cFinishGenPtr(new cFinishGenFluidSprings(m_Seed, E_BLOCK_LAVA, a_IniFile, Dimension)));
}
else if (NoCaseCompare(finisher, "Lilypads") == 0)
{
@ -387,11 +383,11 @@ void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile)
AllowedBlocks.push_back(E_BLOCK_WATER);
AllowedBlocks.push_back(E_BLOCK_STATIONARY_WATER);
m_FinishGens.push_back(cFinishGenPtr(new cFinishGenSingleTopBlock(Seed, E_BLOCK_LILY_PAD, AllowedBiomes, 4, AllowedBlocks)));
m_FinishGens.push_back(cFinishGenPtr(new cFinishGenSingleTopBlock(m_Seed, E_BLOCK_LILY_PAD, AllowedBiomes, 4, AllowedBlocks)));
}
else if (NoCaseCompare(finisher, "MarbleCaves") == 0)
{
m_FinishGens.push_back(cFinishGenPtr(new cStructGenMarbleCaves(Seed)));
m_FinishGens.push_back(cFinishGenPtr(new cStructGenMarbleCaves(m_Seed)));
}
else if (NoCaseCompare(finisher, "MineShafts") == 0)
{
@ -402,22 +398,22 @@ void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile)
int ChanceCrossing = a_IniFile.GetValueSetI("Generator", "MineShaftsChanceCrossing", 200);
int ChanceStaircase = a_IniFile.GetValueSetI("Generator", "MineShaftsChanceStaircase", 200);
m_FinishGens.push_back(cFinishGenPtr(new cStructGenMineShafts(
Seed, GridSize, MaxOffset, MaxSystemSize,
m_Seed, GridSize, MaxOffset, MaxSystemSize,
ChanceCorridor, ChanceCrossing, ChanceStaircase
)));
}
else if (NoCaseCompare(finisher, "NaturalPatches") == 0)
{
m_FinishGens.push_back(std::make_shared<cFinishGenOreNests>(Seed + 1, cFinishGenOreNests::DefaultNaturalPatches()));
m_FinishGens.push_back(std::make_shared<cFinishGenOreNests>(m_Seed + 1, cFinishGenOreNests::DefaultNaturalPatches()));
}
else if (NoCaseCompare(finisher, "NetherClumpFoliage") == 0)
{
m_FinishGens.push_back(cFinishGenPtr(new cFinishGenNetherClumpFoliage(Seed)));
m_FinishGens.push_back(cFinishGenPtr(new cFinishGenNetherClumpFoliage(m_Seed)));
}
else if (NoCaseCompare(*itr, "NetherForts") == 0)
{
LOGINFO("The NetherForts finisher is obsolete, you should use \"PieceStructures: NetherFort\" instead.");
auto gen = std::make_shared<cPieceStructuresGen>(Seed);
auto gen = std::make_shared<cPieceStructuresGen>(m_Seed);
if (gen->Initialize("NetherFort", seaLevel, m_BiomeGen, m_CompositedHeightCache))
{
m_FinishGens.push_back(gen);
@ -425,15 +421,15 @@ void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile)
}
else if (NoCaseCompare(finisher, "NetherOreNests") == 0)
{
m_FinishGens.push_back(std::make_shared<cFinishGenOreNests>(Seed + 2, cFinishGenOreNests::DefaultNetherOres()));
m_FinishGens.push_back(std::make_shared<cFinishGenOreNests>(m_Seed + 2, cFinishGenOreNests::DefaultNetherOres()));
}
else if (NoCaseCompare(finisher, "OreNests") == 0)
{
m_FinishGens.push_back(std::make_shared<cFinishGenOreNests>(Seed + 3, cFinishGenOreNests::DefaultOverworldOres()));
m_FinishGens.push_back(std::make_shared<cFinishGenOreNests>(m_Seed + 3, cFinishGenOreNests::DefaultOverworldOres()));
}
else if (NoCaseCompare(finisher, "OrePockets") == 0)
{
auto gen = std::make_shared<cFinishGenOrePockets>(Seed + 2, cFinishGenOrePockets::DefaultOverworldOres());
auto gen = std::make_shared<cFinishGenOrePockets>(m_Seed + 2, cFinishGenOrePockets::DefaultOverworldOres());
if (gen->Initialize(a_IniFile, "OrePockets"))
{
m_FinishGens.push_back(gen);
@ -442,7 +438,7 @@ void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile)
else if (NoCaseCompare(finisher, "OverworldClumpFlowers") == 0)
{
auto flowers = cFinishGenClumpTopBlock::ParseIniFile(a_IniFile, "OverworldClumpFlowers");
m_FinishGens.push_back(cFinishGenPtr(new cFinishGenClumpTopBlock(Seed, flowers)));
m_FinishGens.push_back(cFinishGenPtr(new cFinishGenClumpTopBlock(m_Seed, flowers)));
}
else if (NoCaseCompare(finisher, "PieceStructures") == 0)
{
@ -452,7 +448,7 @@ void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile)
continue;
}
auto gen = std::make_shared<cPieceStructuresGen>(Seed);
auto gen = std::make_shared<cPieceStructuresGen>(m_Seed);
if (gen->Initialize(split[1], seaLevel, m_BiomeGen, m_CompositedHeightCache))
{
m_FinishGens.push_back(gen);
@ -470,7 +466,7 @@ void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile)
else if (NoCaseCompare(finisher, "RainbowRoads") == 0)
{
LOGINFO("The RainbowRoads finisher is obsolete, you should use \"PieceStructures: RainbowRoads\" instead.");
auto gen = std::make_shared<cPieceStructuresGen>(Seed);
auto gen = std::make_shared<cPieceStructuresGen>(m_Seed);
if (gen->Initialize("RainbowRoads", seaLevel, m_BiomeGen, m_CompositedHeightCache))
{
m_FinishGens.push_back(gen);
@ -478,7 +474,7 @@ void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile)
}
else if (NoCaseCompare(finisher, "Ravines") == 0)
{
m_FinishGens.push_back(cFinishGenPtr(new cStructGenRavines(Seed, 128)));
m_FinishGens.push_back(cFinishGenPtr(new cStructGenRavines(m_Seed, 128)));
}
else if (NoCaseCompare(finisher, "RoughRavines") == 0)
{
@ -499,7 +495,7 @@ void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile)
double MaxCeilingHeightCenter = a_IniFile.GetValueSetF("Generator", "RoughRavinesMaxCeilingHeightCenter", 58);
double MinCeilingHeightCenter = a_IniFile.GetValueSetF("Generator", "RoughRavinesMinCeilingHeightCenter", 36);
m_FinishGens.push_back(cFinishGenPtr(new cRoughRavines(
Seed, MaxSize, MinSize,
m_Seed, MaxSize, MinSize,
static_cast<float>(MaxCenterWidth),
static_cast<float>(MinCenterWidth),
static_cast<float>(MaxRoughness),
@ -517,7 +513,7 @@ void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile)
}
else if (NoCaseCompare(finisher, "SoulsandRims") == 0)
{
m_FinishGens.push_back(cFinishGenPtr(new cFinishGenSoulsandRims(Seed)));
m_FinishGens.push_back(cFinishGenPtr(new cFinishGenSoulsandRims(m_Seed)));
}
else if (NoCaseCompare(finisher, "Snow") == 0)
{
@ -527,20 +523,20 @@ void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile)
{
int MaxCactusHeight = a_IniFile.GetValueI("Plants", "MaxCactusHeight", 3);
int MaxSugarcaneHeight = a_IniFile.GetValueI("Plants", "MaxSugarcaneHeight", 3);
m_FinishGens.push_back(std::make_shared<cFinishGenSprinkleFoliage>(Seed, MaxCactusHeight, MaxSugarcaneHeight));
m_FinishGens.push_back(std::make_shared<cFinishGenSprinkleFoliage>(m_Seed, MaxCactusHeight, MaxSugarcaneHeight));
}
else if (NoCaseCompare(finisher, "TallGrass") == 0)
{
m_FinishGens.push_back(cFinishGenPtr(new cFinishGenTallGrass(Seed)));
m_FinishGens.push_back(cFinishGenPtr(new cFinishGenTallGrass(m_Seed)));
}
else if (NoCaseCompare(finisher, "Trees") == 0)
{
m_FinishGens.push_back(cFinishGenPtr(new cStructGenTrees(Seed, m_BiomeGen, m_ShapeGen, m_CompositionGen)));
m_FinishGens.push_back(cFinishGenPtr(new cStructGenTrees(m_Seed, m_BiomeGen, m_ShapeGen, m_CompositionGen)));
}
else if (NoCaseCompare(finisher, "UnderwaterBases") == 0)
{
LOGINFO("The UnderwaterBases finisher is obsolete, you should use \"PieceStructures: UnderwaterBases\" instead.");
auto gen = std::make_shared<cPieceStructuresGen>(Seed);
auto gen = std::make_shared<cPieceStructuresGen>(m_Seed);
if (gen->Initialize("UnderwaterBases", seaLevel, m_BiomeGen, m_CompositedHeightCache))
{
m_FinishGens.push_back(gen);
@ -556,28 +552,28 @@ void cComposableGenerator::InitFinishGens(cIniFile & a_IniFile)
int MaxDensity = a_IniFile.GetValueSetI("Generator", "VillageMaxDensity", 80);
AString PrefabList = a_IniFile.GetValueSet("Generator", "VillagePrefabs", "PlainsVillage, SandVillage");
auto Prefabs = StringSplitAndTrim(PrefabList, ",");
m_FinishGens.push_back(std::make_shared<cVillageGen>(Seed, GridSize, MaxOffset, MaxDepth, MaxSize, MinDensity, MaxDensity, m_BiomeGen, m_CompositedHeightCache, seaLevel, Prefabs));
m_FinishGens.push_back(std::make_shared<cVillageGen>(m_Seed, GridSize, MaxOffset, MaxDepth, MaxSize, MinDensity, MaxDensity, m_BiomeGen, m_CompositedHeightCache, seaLevel, Prefabs));
}
else if (NoCaseCompare(finisher, "Vines") == 0)
{
int Level = a_IniFile.GetValueSetI("Generator", "VinesLevel", 40);
m_FinishGens.push_back(std::make_shared<cFinishGenVines>(Seed, Level));
m_FinishGens.push_back(std::make_shared<cFinishGenVines>(m_Seed, Level));
}
else if (NoCaseCompare(finisher, "WaterLakes") == 0)
{
int Probability = a_IniFile.GetValueSetI("Generator", "WaterLakesProbability", 25);
m_FinishGens.push_back(cFinishGenPtr(new cStructGenLakes(Seed * 3 + 652, E_BLOCK_STATIONARY_WATER, m_ShapeGen, Probability)));
m_FinishGens.push_back(cFinishGenPtr(new cStructGenLakes(m_Seed * 3 + 652, E_BLOCK_STATIONARY_WATER, m_ShapeGen, Probability)));
}
else if (NoCaseCompare(finisher, "WaterSprings") == 0)
{
m_FinishGens.push_back(cFinishGenPtr(new cFinishGenFluidSprings(Seed, E_BLOCK_WATER, a_IniFile, Dimension)));
m_FinishGens.push_back(cFinishGenPtr(new cFinishGenFluidSprings(m_Seed, E_BLOCK_WATER, a_IniFile, Dimension)));
}
else if (NoCaseCompare(finisher, "WormNestCaves") == 0)
{
int Size = a_IniFile.GetValueSetI("Generator", "WormNestCavesSize", 64);
int Grid = a_IniFile.GetValueSetI("Generator", "WormNestCavesGrid", 96);
int MaxOffset = a_IniFile.GetValueSetI("Generator", "WormNestMaxOffset", 32);
m_FinishGens.push_back(cFinishGenPtr(new cStructGenWormNestCaves(Seed, Size, Grid, MaxOffset)));
m_FinishGens.push_back(cFinishGenPtr(new cStructGenWormNestCaves(m_Seed, Size, Grid, MaxOffset)));
}
else
{

View File

@ -183,17 +183,17 @@ typedef std::list<cFinishGenPtr> cFinishGenList;
class cComposableGenerator :
public cChunkGenerator::cGenerator
public cChunkGenerator
{
typedef cChunkGenerator::cGenerator super;
typedef cChunkGenerator Super;
public:
cComposableGenerator(cChunkGenerator & a_ChunkGenerator);
cComposableGenerator();
// cChunkGenerator::cGenerator overrides:
virtual void Initialize(cIniFile & a_IniFile) override;
virtual void GenerateBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) override;
virtual void DoGenerate(int a_ChunkX, int a_ChunkZ, cChunkDesc & a_ChunkDesc) override;
virtual void Generate(int a_ChunkX, int a_ChunkZ, cChunkDesc & a_ChunkDesc) override;
protected:
// The generator's composition:

View File

@ -34,7 +34,7 @@ public:
{
cChunkDesc::Shape shape;
m_ShapeGen->GenShape(a_ChunkX, a_ChunkZ, shape);
cChunkDesc desc(a_ChunkX, a_ChunkZ);
cChunkDesc desc({a_ChunkX, a_ChunkZ});
m_BiomeGen->GenBiomes(a_ChunkX, a_ChunkZ, desc.GetBiomeMap()); // Need to initialize biomes for the composition gen
desc.SetHeightFromShape(shape);
m_CompositionGen->ComposeTerrain(desc, shape);

View File

@ -12,7 +12,6 @@
#include "FinishGen.h"
#include "../Simulator/FluidSimulator.h" // for cFluidSimulator::CanWashAway()
#include "../Simulator/FireSimulator.h"
#include "../World.h"
#include "../IniFile.h"
#include "../MobSpawner.h"

View File

@ -148,8 +148,8 @@ public:
////////////////////////////////////////////////////////////////////////////////
// cNoise3DGenerator:
cNoise3DGenerator::cNoise3DGenerator(cChunkGenerator & a_ChunkGenerator) :
super(a_ChunkGenerator),
cNoise3DGenerator::cNoise3DGenerator():
Super(),
m_Perlin(1000),
m_Cubic(1000)
{
@ -207,7 +207,7 @@ void cNoise3DGenerator::GenerateBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::Bi
void cNoise3DGenerator::DoGenerate(int a_ChunkX, int a_ChunkZ, cChunkDesc & a_ChunkDesc)
void cNoise3DGenerator::Generate(int a_ChunkX, int a_ChunkZ, cChunkDesc & a_ChunkDesc)
{
NOISE_DATATYPE Noise[17 * 257 * 17];
GenerateNoiseArray(a_ChunkX, a_ChunkZ, Noise);

View File

@ -21,17 +21,17 @@
class cNoise3DGenerator :
public cChunkGenerator::cGenerator
public cChunkGenerator
{
typedef cChunkGenerator::cGenerator super;
typedef cChunkGenerator Super;
public:
cNoise3DGenerator(cChunkGenerator & a_ChunkGenerator);
cNoise3DGenerator();
virtual ~cNoise3DGenerator() override;
virtual void Initialize(cIniFile & a_IniFile) override;
virtual void GenerateBiomes(int a_ChunkX, int a_ChunkZ, cChunkDef::BiomeMap & a_BiomeMap) override;
virtual void DoGenerate(int a_ChunkX, int a_ChunkZ, cChunkDesc & a_ChunkDesc) override;
virtual void Generate(int a_ChunkX, int a_ChunkZ, cChunkDesc & a_ChunkDesc) override;
protected:
// Linear interpolation step sizes, must be divisors of cChunkDef::Width and cChunkDef::Height, respectively:

View File

@ -19,7 +19,7 @@ void cStructGenTrees::GenFinish(cChunkDesc & a_ChunkDesc)
int ChunkX = a_ChunkDesc.GetChunkX();
int ChunkZ = a_ChunkDesc.GetChunkZ();
cChunkDesc WorkerDesc(ChunkX, ChunkZ);
cChunkDesc WorkerDesc({ChunkX, ChunkZ});
// Generate trees:
for (int x = 0; x <= 2; x++)
@ -34,7 +34,7 @@ void cStructGenTrees::GenFinish(cChunkDesc & a_ChunkDesc)
if ((x != 1) || (z != 1))
{
Dest = &WorkerDesc;
WorkerDesc.SetChunkCoords(BaseX, BaseZ);
WorkerDesc.SetChunkCoords({BaseX, BaseZ});
// TODO: This may cause a lot of wasted calculations, instead of pulling data out of a single (cChunkDesc) cache

View File

@ -5,7 +5,6 @@
#include "Globals.h"
#include "Trees.h"
#include "../World.h"
@ -1056,89 +1055,3 @@ void GetSmallJungleTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise &
PushCoordBlocks(a_BlockX, hei, a_BlockZ, a_OtherBlocks, BigO1, ARRAYCOUNT(BigO1), E_BLOCK_LEAVES, E_META_LEAVES_JUNGLE);
a_OtherBlocks.push_back(sSetBlock(a_BlockX, hei, a_BlockZ, E_BLOCK_LEAVES, E_META_LEAVES_JUNGLE));
}
bool GetLargeTreeAdjustment(cWorld & a_World, int & a_X, int & a_Y, int & a_Z, NIBBLETYPE a_Meta)
{
bool IsLarge = true;
a_Meta = a_Meta & 0x07;
// Check to see if we are the northwest corner
for (int x = 0; x < 2; ++x)
{
for (int z = 0; z < 2; ++z)
{
NIBBLETYPE meta;
BLOCKTYPE type;
a_World.GetBlockTypeMeta(a_X + x, a_Y, a_Z + z, type, meta);
IsLarge = IsLarge && (type == E_BLOCK_SAPLING) && ((a_Meta & meta) == a_Meta);
}
}
if (IsLarge)
{
return true;
}
IsLarge = true;
// Check to see if we are the southwest corner
for (int x = 0; x < 2; ++x)
{
for (int z = 0; z > -2; --z)
{
NIBBLETYPE meta;
BLOCKTYPE type;
a_World.GetBlockTypeMeta(a_X + x, a_Y, a_Z + z, type, meta);
IsLarge = IsLarge && (type == E_BLOCK_SAPLING) && ((a_Meta & meta) == a_Meta);
}
}
if (IsLarge)
{
--a_Z;
return true;
}
IsLarge = true;
// Check to see if we are the southeast corner
for (int x = 0; x > -2; --x)
{
for (int z = 0; z > -2; --z)
{
NIBBLETYPE meta;
BLOCKTYPE type;
a_World.GetBlockTypeMeta(a_X + x, a_Y, a_Z + z, type, meta);
IsLarge = IsLarge && (type == E_BLOCK_SAPLING) && ((a_Meta & meta) == a_Meta);
}
}
if (IsLarge)
{
--a_Z;
--a_X;
return true;
}
IsLarge = true;
// Check to see if we are the northeast corner
for (int x = 0; x > -2; --x)
{
for (int z = 0; z < 2; ++z)
{
NIBBLETYPE meta;
BLOCKTYPE type;
a_World.GetBlockTypeMeta(a_X + x, a_Y, a_Z + z, type, meta);
IsLarge = IsLarge && (type == E_BLOCK_SAPLING) && ((a_Meta & meta) == a_Meta);
}
}
if (IsLarge)
{
--a_X;
}
return IsLarge;
}

View File

@ -19,8 +19,6 @@ logs can overwrite others(leaves), but others shouldn't overwrite logs. This is
#include "../Noise/Noise.h"
class cWorld;
@ -104,9 +102,3 @@ void GetLargeJungleTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise &
/** Generates an image of a small jungle tree (1x1 trunk) */
void GetSmallJungleTreeImage(int a_BlockX, int a_BlockY, int a_BlockZ, cNoise & a_Noise, int a_Seq, sSetBlockVector & a_LogBlocks, sSetBlockVector & a_OtherBlocks);
/** Moves the x and z coordinants to the north-west corner of a 2x2 of saplings. Returns true if a 2x2 was found, otherwise it returns false */
bool GetLargeTreeAdjustment(cWorld & a_World, int & a_X, int & a_Y, int & a_Z, NIBBLETYPE a_Meta);

View File

@ -237,7 +237,7 @@ void cLightingThread::LightChunk(cLightingChunkStay & a_Item)
{
if (a_Item.m_CallbackAfter != nullptr)
{
a_Item.m_CallbackAfter->Call(a_Item.m_ChunkX, a_Item.m_ChunkZ, true);
a_Item.m_CallbackAfter->Call({a_Item.m_ChunkX, a_Item.m_ChunkZ}, true);
}
return;
}
@ -318,7 +318,7 @@ void cLightingThread::LightChunk(cLightingChunkStay & a_Item)
if (a_Item.m_CallbackAfter != nullptr)
{
a_Item.m_CallbackAfter->Call(a_Item.m_ChunkX, a_Item.m_ChunkZ, true);
a_Item.m_CallbackAfter->Call({a_Item.m_ChunkX, a_Item.m_ChunkZ}, true);
}
}

View File

@ -21,9 +21,9 @@ protected:
std::shared_ptr<cSpawnPrepare> m_SpawnPrepare;
virtual void Call(int a_ChunkX, int a_ChunkZ, bool a_IsSuccess) override
virtual void Call(cChunkCoords a_Coords, bool a_IsSuccess) override
{
m_SpawnPrepare->PreparedChunkCallback(a_ChunkX, a_ChunkZ);
m_SpawnPrepare->PreparedChunkCallback(a_Coords.m_ChunkX, a_Coords.m_ChunkZ);
}
};

View File

@ -1648,13 +1648,13 @@ void cWorld::GrowTreeFromSapling(int a_X, int a_Y, int a_Z, NIBBLETYPE a_Sapling
case E_META_SAPLING_ACACIA: GetAcaciaTreeImage (a_X, a_Y, a_Z, Noise, WorldAge, Logs, Other); break;
case E_META_SAPLING_JUNGLE:
{
bool IsLarge = GetLargeTreeAdjustment(*this, a_X, a_Y, a_Z, a_SaplingMeta);
bool IsLarge = GetLargeTreeAdjustment(a_X, a_Y, a_Z, a_SaplingMeta);
GetJungleTreeImage (a_X, a_Y, a_Z, Noise, WorldAge, Logs, Other, IsLarge);
break;
}
case E_META_SAPLING_DARK_OAK:
{
if (!GetLargeTreeAdjustment(*this, a_X, a_Y, a_Z, a_SaplingMeta))
if (!GetLargeTreeAdjustment(a_X, a_Y, a_Z, a_SaplingMeta))
{
return;
}
@ -1672,6 +1672,92 @@ void cWorld::GrowTreeFromSapling(int a_X, int a_Y, int a_Z, NIBBLETYPE a_Sapling
bool cWorld::GetLargeTreeAdjustment(int & a_X, int & a_Y, int & a_Z, NIBBLETYPE a_Meta)
{
bool IsLarge = true;
a_Meta = a_Meta & 0x07;
// Check to see if we are the northwest corner
for (int x = 0; x < 2; ++x)
{
for (int z = 0; z < 2; ++z)
{
NIBBLETYPE meta;
BLOCKTYPE type;
GetBlockTypeMeta(a_X + x, a_Y, a_Z + z, type, meta);
IsLarge = IsLarge && (type == E_BLOCK_SAPLING) && ((a_Meta & meta) == a_Meta);
}
}
if (IsLarge)
{
return true;
}
IsLarge = true;
// Check to see if we are the southwest corner
for (int x = 0; x < 2; ++x)
{
for (int z = 0; z > -2; --z)
{
NIBBLETYPE meta;
BLOCKTYPE type;
GetBlockTypeMeta(a_X + x, a_Y, a_Z + z, type, meta);
IsLarge = IsLarge && (type == E_BLOCK_SAPLING) && ((a_Meta & meta) == a_Meta);
}
}
if (IsLarge)
{
--a_Z;
return true;
}
IsLarge = true;
// Check to see if we are the southeast corner
for (int x = 0; x > -2; --x)
{
for (int z = 0; z > -2; --z)
{
NIBBLETYPE meta;
BLOCKTYPE type;
GetBlockTypeMeta(a_X + x, a_Y, a_Z + z, type, meta);
IsLarge = IsLarge && (type == E_BLOCK_SAPLING) && ((a_Meta & meta) == a_Meta);
}
}
if (IsLarge)
{
--a_Z;
--a_X;
return true;
}
IsLarge = true;
// Check to see if we are the northeast corner
for (int x = 0; x > -2; --x)
{
for (int z = 0; z < 2; ++z)
{
NIBBLETYPE meta;
BLOCKTYPE type;
GetBlockTypeMeta(a_X + x, a_Y, a_Z + z, type, meta);
IsLarge = IsLarge && (type == E_BLOCK_SAPLING) && ((a_Meta & meta) == a_Meta);
}
}
if (IsLarge)
{
--a_X;
}
return IsLarge;
}
void cWorld::GrowTreeByBiome(int a_X, int a_Y, int a_Z)
{
cNoise Noise(m_Generator.GetSeed());
@ -2514,7 +2600,7 @@ void cWorld::QueueSetChunkData(cSetChunkDataPtr a_SetChunkData)
if (!a_SetChunkData->AreBiomesValid())
{
// The biomes are not assigned, get them from the generator:
m_Generator.GenerateBiomes(a_SetChunkData->GetChunkX(), a_SetChunkData->GetChunkZ(), a_SetChunkData->GetBiomes());
m_Generator.GenerateBiomes({a_SetChunkData->GetChunkX(), a_SetChunkData->GetChunkZ()}, a_SetChunkData->GetBiomes());
a_SetChunkData->MarkBiomesValid();
}
@ -3157,7 +3243,7 @@ void cWorld::RegenerateChunk(int a_ChunkX, int a_ChunkZ)
{
m_ChunkMap->MarkChunkRegenerating(a_ChunkX, a_ChunkZ);
m_Generator.QueueGenerateChunk(a_ChunkX, a_ChunkZ, true);
m_Generator.QueueGenerateChunk({a_ChunkX, a_ChunkZ}, true);
}
@ -3770,27 +3856,27 @@ void cWorld::cChunkGeneratorCallbacks::OnChunkGenerated(cChunkDesc & a_ChunkDesc
bool cWorld::cChunkGeneratorCallbacks::IsChunkValid(int a_ChunkX, int a_ChunkZ)
bool cWorld::cChunkGeneratorCallbacks::IsChunkValid(cChunkCoords a_Coords)
{
return m_World->IsChunkValid(a_ChunkX, a_ChunkZ);
return m_World->IsChunkValid(a_Coords.m_ChunkX, a_Coords.m_ChunkZ);
}
bool cWorld::cChunkGeneratorCallbacks::IsChunkQueued(int a_ChunkX, int a_ChunkZ)
bool cWorld::cChunkGeneratorCallbacks::IsChunkQueued(cChunkCoords a_Coords)
{
return m_World->IsChunkQueued(a_ChunkX, a_ChunkZ);
return m_World->IsChunkQueued(a_Coords.m_ChunkX, a_Coords.m_ChunkZ);
}
bool cWorld::cChunkGeneratorCallbacks::HasChunkAnyClients(int a_ChunkX, int a_ChunkZ)
bool cWorld::cChunkGeneratorCallbacks::HasChunkAnyClients(cChunkCoords a_Coords)
{
return m_World->HasChunkAnyClients(a_ChunkX, a_ChunkZ);
return m_World->HasChunkAnyClients(a_Coords.m_ChunkX, a_Coords.m_ChunkZ);
}

View File

@ -8,7 +8,7 @@
#include "Simulator/SimulatorManager.h"
#include "ChunkMap.h"
#include "WorldStorage/WorldStorage.h"
#include "Generating/ChunkGenerator.h"
#include "ChunkGeneratorThread.h"
#include "ChunkSender.h"
#include "Defines.h"
#include "LightingThread.h"
@ -824,7 +824,7 @@ public:
// tolua_end
cChunkGenerator & GetGenerator(void) { return m_Generator; }
cChunkGeneratorThread & GetGenerator(void) { return m_Generator; }
cWorldStorage & GetStorage (void) { return m_Storage; }
cChunkMap * GetChunkMap (void) { return m_ChunkMap.get(); }
@ -883,16 +883,16 @@ private:
/** Implementation of the callbacks that the ChunkGenerator uses to store new chunks and interface to plugins */
class cChunkGeneratorCallbacks :
public cChunkGenerator::cChunkSink,
public cChunkGenerator::cPluginInterface
public cChunkGeneratorThread::cChunkSink,
public cChunkGeneratorThread::cPluginInterface
{
cWorld * m_World;
// cChunkSink overrides:
virtual void OnChunkGenerated (cChunkDesc & a_ChunkDesc) override;
virtual bool IsChunkValid (int a_ChunkX, int a_ChunkZ) override;
virtual bool HasChunkAnyClients(int a_ChunkX, int a_ChunkZ) override;
virtual bool IsChunkQueued (int a_ChunkX, int a_ChunkZ) override;
virtual bool IsChunkValid (cChunkCoords a_Coords) override;
virtual bool HasChunkAnyClients(cChunkCoords a_Coords) override;
virtual bool IsChunkQueued (cChunkCoords a_Coords) override;
// cPluginInterface overrides:
virtual void CallHookChunkGenerating(cChunkDesc & a_ChunkDesc) override;
@ -1031,8 +1031,8 @@ private:
Only used when this world is an Overworld. */
AString m_LinkedEndWorldName;
cChunkGenerator m_Generator;
/** The thread responsible for generating chunks. */
cChunkGeneratorThread m_Generator;
cScoreboard m_Scoreboard;
cMapManager m_MapManager;
@ -1143,4 +1143,8 @@ private:
Modifies the a_SetChunkData - moves the entities contained in it into the chunk. */
void SetChunkData(cSetChunkData & a_SetChunkData);
/** Checks if the sapling at the specified block coord is a part of a large-tree sapling (2x2).
If so, adjusts the X and Z coords so that they point to the northwest (XM ZM) corner of the sapling area and returns true.
Returns false if not a part of large-tree sapling. */
bool GetLargeTreeAdjustment(int & a_BlockX, int & a_BlockY, int & a_BlockZ, NIBBLETYPE a_SaplingMeta);
}; // tolua_export

View File

@ -240,7 +240,7 @@ bool cWorldStorage::LoadOneChunk(void)
// Call the callback, if specified:
if (ToLoad.m_Callback != nullptr)
{
ToLoad.m_Callback->Call(ToLoad.m_ChunkX, ToLoad.m_ChunkZ, res);
ToLoad.m_Callback->Call({ToLoad.m_ChunkX, ToLoad.m_ChunkZ}, res);
}
return res;
}
@ -274,7 +274,7 @@ bool cWorldStorage::SaveOneChunk(void)
// Call the callback, if specified:
if (ToSave.m_Callback != nullptr)
{
ToSave.m_Callback->Call(ToSave.m_ChunkX, ToSave.m_ChunkZ, Status);
ToSave.m_Callback->Call({ToSave.m_ChunkX, ToSave.m_ChunkZ}, Status);
}
return true;
}

View File

@ -15,19 +15,11 @@ set (SHARED_SRCS
${CMAKE_SOURCE_DIR}/src/StringCompression.cpp
${CMAKE_SOURCE_DIR}/src/StringUtils.cpp
${CMAKE_SOURCE_DIR}/src/Bindings/LuaState.cpp
${CMAKE_SOURCE_DIR}/src/Generating/ChunkDesc.cpp
${CMAKE_SOURCE_DIR}/src/Generating/PiecePool.cpp
${CMAKE_SOURCE_DIR}/src/Generating/Prefab.cpp
${CMAKE_SOURCE_DIR}/src/Generating/PrefabPiecePool.cpp
${CMAKE_SOURCE_DIR}/src/Generating/VerticalLimit.cpp
${CMAKE_SOURCE_DIR}/src/Generating/VerticalStrategy.cpp
${CMAKE_SOURCE_DIR}/src/Bindings/LuaState.cpp # Needed for PrefabPiecePool loading
${CMAKE_SOURCE_DIR}/src/Noise/Noise.cpp
${CMAKE_SOURCE_DIR}/src/OSSupport/CriticalSection.cpp
${CMAKE_SOURCE_DIR}/src/OSSupport/Event.cpp
${CMAKE_SOURCE_DIR}/src/OSSupport/CriticalSection.cpp # Needed for LuaState
${CMAKE_SOURCE_DIR}/src/OSSupport/File.cpp
${CMAKE_SOURCE_DIR}/src/OSSupport/GZipFile.cpp
${CMAKE_SOURCE_DIR}/src/OSSupport/StackTrace.cpp
@ -37,6 +29,38 @@ set (SHARED_SRCS
${CMAKE_SOURCE_DIR}/src/WorldStorage/SchematicFileSerializer.cpp
)
set (GENERATING_SRCS
${CMAKE_SOURCE_DIR}/src/Generating/BioGen.cpp
${CMAKE_SOURCE_DIR}/src/Generating/Caves.cpp
${CMAKE_SOURCE_DIR}/src/Generating/ChunkDesc.cpp
${CMAKE_SOURCE_DIR}/src/Generating/ChunkGenerator.cpp
${CMAKE_SOURCE_DIR}/src/Generating/CompoGen.cpp
${CMAKE_SOURCE_DIR}/src/Generating/CompoGenBiomal.cpp
${CMAKE_SOURCE_DIR}/src/Generating/ComposableGenerator.cpp
${CMAKE_SOURCE_DIR}/src/Generating/DistortedHeightmap.cpp
${CMAKE_SOURCE_DIR}/src/Generating/DungeonRoomsFinisher.cpp
${CMAKE_SOURCE_DIR}/src/Generating/EndGen.cpp
${CMAKE_SOURCE_DIR}/src/Generating/FinishGen.cpp
${CMAKE_SOURCE_DIR}/src/Generating/GridStructGen.cpp
${CMAKE_SOURCE_DIR}/src/Generating/HeiGen.cpp
${CMAKE_SOURCE_DIR}/src/Generating/MineShafts.cpp
${CMAKE_SOURCE_DIR}/src/Generating/Noise3DGenerator.cpp
${CMAKE_SOURCE_DIR}/src/Generating/PieceGeneratorBFSTree.cpp
${CMAKE_SOURCE_DIR}/src/Generating/PiecePool.cpp
${CMAKE_SOURCE_DIR}/src/Generating/PieceStructuresGen.cpp
${CMAKE_SOURCE_DIR}/src/Generating/Prefab.cpp
${CMAKE_SOURCE_DIR}/src/Generating/PrefabPiecePool.cpp
${CMAKE_SOURCE_DIR}/src/Generating/PrefabStructure.cpp
${CMAKE_SOURCE_DIR}/src/Generating/Ravines.cpp
${CMAKE_SOURCE_DIR}/src/Generating/RoughRavines.cpp
${CMAKE_SOURCE_DIR}/src/Generating/StructGen.cpp
${CMAKE_SOURCE_DIR}/src/Generating/Trees.cpp
${CMAKE_SOURCE_DIR}/src/Generating/TwoHeights.cpp
${CMAKE_SOURCE_DIR}/src/Generating/VerticalLimit.cpp
${CMAKE_SOURCE_DIR}/src/Generating/VerticalStrategy.cpp
${CMAKE_SOURCE_DIR}/src/Generating/VillageGen.cpp
)
set (SHARED_HDRS
${CMAKE_SOURCE_DIR}/src/BiomeDef.h
${CMAKE_SOURCE_DIR}/src/BlockArea.h
@ -48,13 +72,6 @@ set (SHARED_HDRS
${CMAKE_SOURCE_DIR}/src/Bindings/LuaState.h
${CMAKE_SOURCE_DIR}/src/Generating/ChunkDesc.h
${CMAKE_SOURCE_DIR}/src/Generating/PiecePool.h
${CMAKE_SOURCE_DIR}/src/Generating/Prefab.h
${CMAKE_SOURCE_DIR}/src/Generating/PrefabPiecePool.h
${CMAKE_SOURCE_DIR}/src/Generating/VerticalLimit.h
${CMAKE_SOURCE_DIR}/src/Generating/VerticalStrategy.h
${CMAKE_SOURCE_DIR}/src/Noise/Noise.h
${CMAKE_SOURCE_DIR}/src/OSSupport/CriticalSection.h
@ -68,6 +85,42 @@ set (SHARED_HDRS
${CMAKE_SOURCE_DIR}/src/WorldStorage/SchematicFileSerializer.h
)
set (GENERATING_HDRS
${CMAKE_SOURCE_DIR}/src/Generating/BioGen.h
${CMAKE_SOURCE_DIR}/src/Generating/Caves.h
${CMAKE_SOURCE_DIR}/src/Generating/ChunkDesc.h
${CMAKE_SOURCE_DIR}/src/Generating/ChunkGenerator.h
${CMAKE_SOURCE_DIR}/src/Generating/CompoGen.h
${CMAKE_SOURCE_DIR}/src/Generating/CompoGenBiomal.h
${CMAKE_SOURCE_DIR}/src/Generating/ComposableGenerator.h
${CMAKE_SOURCE_DIR}/src/Generating/CompositedHeiGen.h
${CMAKE_SOURCE_DIR}/src/Generating/DistortedHeightmap.h
${CMAKE_SOURCE_DIR}/src/Generating/DungeonRoomsFinisher.h
${CMAKE_SOURCE_DIR}/src/Generating/EndGen.h
${CMAKE_SOURCE_DIR}/src/Generating/FinishGen.h
${CMAKE_SOURCE_DIR}/src/Generating/GridStructGen.h
${CMAKE_SOURCE_DIR}/src/Generating/HeiGen.h
${CMAKE_SOURCE_DIR}/src/Generating/IntGen.h
${CMAKE_SOURCE_DIR}/src/Generating/MineShafts.h
${CMAKE_SOURCE_DIR}/src/Generating/Noise3DGenerator.h
${CMAKE_SOURCE_DIR}/src/Generating/PieceGeneratorBFSTree.h
${CMAKE_SOURCE_DIR}/src/Generating/PiecePool.h
${CMAKE_SOURCE_DIR}/src/Generating/PieceStructuresGen.h
${CMAKE_SOURCE_DIR}/src/Generating/Prefab.h
${CMAKE_SOURCE_DIR}/src/Generating/PrefabPiecePool.h
${CMAKE_SOURCE_DIR}/src/Generating/PrefabStructure.h
${CMAKE_SOURCE_DIR}/src/Generating/ProtIntGen.h
${CMAKE_SOURCE_DIR}/src/Generating/Ravines.h
${CMAKE_SOURCE_DIR}/src/Generating/RoughRavines.h
${CMAKE_SOURCE_DIR}/src/Generating/ShapeGen.cpp
${CMAKE_SOURCE_DIR}/src/Generating/StructGen.h
${CMAKE_SOURCE_DIR}/src/Generating/Trees.h
${CMAKE_SOURCE_DIR}/src/Generating/TwoHeights.h
${CMAKE_SOURCE_DIR}/src/Generating/VerticalLimit.h
${CMAKE_SOURCE_DIR}/src/Generating/VerticalStrategy.h
${CMAKE_SOURCE_DIR}/src/Generating/VillageGen.h
)
set (STUBS
Stubs.cpp
LuaState_Typedefs.inc
@ -83,9 +136,16 @@ endif()
add_library(GeneratorTestingSupport STATIC ${SHARED_SRCS} ${SHARED_HDRS} ${STUBS})
add_library(GeneratorTestingSupport STATIC
${SHARED_SRCS}
${SHARED_HDRS}
${GENERATING_SRCS}
${GENERATING_HDRS}
${STUBS}
)
target_link_libraries(GeneratorTestingSupport tolualib zlib fmt::fmt)
source_group("Stubs" FILES ${STUBS})
source_group("Generating" FILES ${GENERATING_HDRS} ${GENERATING_SRCS})
@ -93,18 +153,32 @@ source_group("Stubs" FILES ${STUBS})
# LoadablePieces test:
source_group("Data files" FILES Test.cubeset Test1.schematic)
add_executable(LoadablePieces LoadablePieces.cpp Test.cubeset Test1.schematic)
add_executable(LoadablePieces
LoadablePieces.cpp
Test.cubeset
Test1.schematic
)
target_link_libraries(LoadablePieces GeneratorTestingSupport)
add_test(NAME LoadablePieces-test WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMAND LoadablePieces)
add_test(
NAME LoadablePieces-test
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMAND LoadablePieces
)
# PieceRotation test:
add_executable(PieceRotation PieceRotationTest.cpp)
add_executable(PieceRotation
PieceRotationTest.cpp
)
target_link_libraries(PieceRotation GeneratorTestingSupport)
add_test(NAME PieceRotation-test WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMAND PieceRotation)
add_test(
NAME PieceRotation-test
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMAND PieceRotation
)
@ -113,11 +187,13 @@ add_test(NAME PieceRotation-test WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} C
# PieceGeneratorBFSTree test:
add_executable(PieceGeneratorBFSTree
PieceGeneratorBFSTreeTest.cpp
${CMAKE_SOURCE_DIR}/src/Generating/PieceGeneratorBFSTree.cpp
${CMAKE_SOURCE_DIR}/src/Generating/PieceGeneratorBFSTree.h
)
target_link_libraries(PieceGeneratorBFSTree GeneratorTestingSupport)
add_test(NAME PieceGeneratorBFSTree-test WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/Server/Prefabs/PieceStructures COMMAND PieceGeneratorBFSTree)
add_test(
NAME PieceGeneratorBFSTree-test
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/Server/Prefabs/PieceStructures
COMMAND PieceGeneratorBFSTree
)