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:
parent
f021e2fe22
commit
a2ffa432b3
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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;
|
||||
} ;
|
||||
|
||||
|
||||
|
264
src/ChunkGeneratorThread.cpp
Normal file
264
src/ChunkGeneratorThread.cpp
Normal 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
159
src/ChunkGeneratorThread.h
Normal 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);
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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());
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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:
|
||||
|
@ -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);
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
106
src/World.cpp
106
src/World.cpp
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
22
src/World.h
22
src/World.h
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user