Cuberite
A lightweight, fast and extensible game server for Minecraft
ChunkGeneratorThread.cpp
Go to the documentation of this file.
1 #include "Globals.h"
2 #include "ChunkGeneratorThread.h"
4 #include "Generating/ChunkDesc.h"
5 
6 
7 
8 
9 
11 const size_t QUEUE_WARNING_LIMIT = 1000;
12 
14 const size_t QUEUE_SKIP_LIMIT = 500;
15 
16 
17 
18 
19 
21  Super("Chunk Generator"),
22  m_Generator(nullptr),
23  m_PluginInterface(nullptr),
24  m_ChunkSink(nullptr)
25 {
26 }
27 
28 
29 
30 
31 
33 {
34  Stop();
35 }
36 
37 
38 
39 
40 
41 bool cChunkGeneratorThread::Initialize(cPluginInterface & a_PluginInterface, cChunkSink & a_ChunkSink, cIniFile & a_IniFile)
42 {
43  m_PluginInterface = &a_PluginInterface;
44  m_ChunkSink = &a_ChunkSink;
45 
47  if (m_Generator == nullptr)
48  {
49  LOGERROR("Generator could not start, aborting the server");
50  return false;
51  }
52  return true;
53 }
54 
55 
56 
57 
58 
60 {
61  m_ShouldTerminate = true;
62  m_Event.Set();
63  m_evtRemoved.Set(); // Wake up anybody waiting for empty queue
64  Super::Stop();
65  m_Generator.reset();
66 }
67 
68 
69 
70 
71 
73  cChunkCoords a_Coords,
74  bool a_ForceRegeneration,
75  cChunkCoordCallback * a_Callback
76 )
77 {
78  ASSERT(m_ChunkSink->IsChunkQueued(a_Coords));
79 
80  {
81  cCSLock Lock(m_CS);
82 
83  // Add to queue, issue a warning if too many:
84  if (m_Queue.size() >= QUEUE_WARNING_LIMIT)
85  {
86  LOGWARN("WARNING: Adding chunk %s to generation queue; Queue is too big! (%zu)", a_Coords.ToString().c_str(), m_Queue.size());
87  }
88  m_Queue.emplace_back(a_Coords, a_ForceRegeneration, a_Callback);
89  }
90 
91  m_Event.Set();
92 }
93 
94 
95 
96 
97 
99 {
100  if (m_Generator != nullptr)
101  {
102  m_Generator->GenerateBiomes(a_Coords, a_BiomeMap);
103  }
104 }
105 
106 
107 
108 
109 
111 {
112  cCSLock Lock(m_CS);
113  while (!m_ShouldTerminate && !m_Queue.empty())
114  {
115  cCSUnlock Unlock(Lock);
116  m_evtRemoved.Wait();
117  }
118 }
119 
120 
121 
122 
123 
125 {
126  cCSLock Lock(m_CS);
127  return m_Queue.size();
128 }
129 
130 
131 
132 
133 
135 {
136  return m_Generator->GetSeed();
137 }
138 
139 
140 
141 
142 
143 EMCSBiome cChunkGeneratorThread::GetBiomeAt(int a_BlockX, int a_BlockZ)
144 {
145  ASSERT(m_Generator != nullptr);
146  return m_Generator->GetBiomeAt(a_BlockX, a_BlockZ);
147 }
148 
149 
150 
151 
152 
154 {
155  // To be able to display performance information, the generator counts the chunks generated.
156  // When the queue gets empty, the count is reset, so that waiting for the queue is not counted into the total time.
157  int NumChunksGenerated = 0; // Number of chunks generated since the queue was last empty
158  clock_t GenerationStart = clock(); // Clock tick when the queue started to fill
159  clock_t LastReportTick = clock(); // Clock tick of the last report made (so that performance isn't reported too often)
160 
161  while (!m_ShouldTerminate)
162  {
163  cCSLock Lock(m_CS);
164  while (m_Queue.empty())
165  {
166  if ((NumChunksGenerated > 16) && (clock() - LastReportTick > CLOCKS_PER_SEC))
167  {
168  /* LOG("Chunk generator performance: %.2f ch / sec (%d ch total)",
169  static_cast<double>(NumChunksGenerated) * CLOCKS_PER_SEC/ (clock() - GenerationStart),
170  NumChunksGenerated
171  ); */
172  }
173  cCSUnlock Unlock(Lock);
174  m_Event.Wait();
175  if (m_ShouldTerminate)
176  {
177  return;
178  }
179  NumChunksGenerated = 0;
180  GenerationStart = clock();
181  LastReportTick = clock();
182  }
183 
184  if (m_Queue.empty())
185  {
186  // Sometimes the queue remains empty
187  // If so, we can't do any front() operations on it!
188  continue;
189  }
190 
191  auto item = m_Queue.front(); // Get next chunk from the queue
192  bool SkipEnabled = (m_Queue.size() > QUEUE_SKIP_LIMIT);
193  m_Queue.erase(m_Queue.begin()); // Remove the item from the queue
194  Lock.Unlock(); // Unlock ASAP
195  m_evtRemoved.Set();
196 
197  // Display perf info once in a while:
198  if ((NumChunksGenerated > 512) && (clock() - LastReportTick > 2 * CLOCKS_PER_SEC))
199  {
200  LOG("Chunk generator performance: %.2f ch / sec (%d ch total)",
201  static_cast<double>(NumChunksGenerated) * CLOCKS_PER_SEC / (clock() - GenerationStart),
202  NumChunksGenerated
203  );
204  LastReportTick = clock();
205  }
206 
207  // Skip the chunk if it's already generated and regeneration is not forced. Report as success:
208  if (!item.m_ForceRegeneration && m_ChunkSink->IsChunkValid(item.m_Coords))
209  {
210  LOGD("Chunk %s already generated, skipping generation", item.m_Coords.ToString().c_str());
211  if (item.m_Callback != nullptr)
212  {
213  item.m_Callback->Call(item.m_Coords, true);
214  }
215  continue;
216  }
217 
218  // Skip the chunk if the generator is overloaded:
219  if (SkipEnabled && !m_ChunkSink->HasChunkAnyClients(item.m_Coords))
220  {
221  LOGWARNING("Chunk generator overloaded, skipping chunk %s", item.m_Coords.ToString().c_str());
222  if (item.m_Callback != nullptr)
223  {
224  item.m_Callback->Call(item.m_Coords, false);
225  }
226  continue;
227  }
228 
229  // Generate the chunk:
230  DoGenerate(item.m_Coords);
231  if (item.m_Callback != nullptr)
232  {
233  item.m_Callback->Call(item.m_Coords, true);
234  }
235  NumChunksGenerated++;
236  } // while (!bStop)
237 }
238 
239 
240 
241 
242 
244 {
245  ASSERT(m_PluginInterface != nullptr);
246  ASSERT(m_ChunkSink != nullptr);
247 
248  cChunkDesc ChunkDesc(a_Coords);
250  m_Generator->Generate(ChunkDesc);
252 
253  #ifndef NDEBUG
254  // Verify that the generator has produced valid data:
255  ChunkDesc.VerifyHeightmap();
256  #endif
257 
258  m_ChunkSink->OnChunkGenerated(ChunkDesc);
259 }
EMCSBiome
Biome IDs The first batch corresponds to the clientside biomes, used by MineCraft.
Definition: BiomeDef.h:18
const size_t QUEUE_WARNING_LIMIT
If the generation queue size exceeds this number, a warning will be output.
const size_t QUEUE_SKIP_LIMIT
If the generation queue size exceeds this number, chunks with no clients will be skipped.
#define ASSERT(x)
Definition: Globals.h:276
void LOGERROR(std::string_view a_Format, const Args &... args)
Definition: LoggerSimple.h:73
void LOGWARNING(std::string_view a_Format, const Args &... args)
Definition: LoggerSimple.h:67
void LOG(std::string_view a_Format, const Args &... args)
Definition: LoggerSimple.h:55
#define LOGWARN
Definition: LoggerSimple.h:88
#define LOGD
Definition: LoggerSimple.h:83
Wraps the chunk coords into a single structure.
Definition: ChunkDef.h:57
AString ToString() const
Returns a string that describes the chunk coords, suitable for logging.
Definition: ChunkDef.h:92
EMCSBiome BiomeMap[Width *Width]
The type used for any biomemap operations and storage inside Cuberite, using Cuberite biomes (need no...
Definition: ChunkDef.h:137
Interface class used as a callback for operations that involve chunk coords.
Definition: ChunkDef.h:467
cEvent m_evtRemoved
Set when an item is removed from the queue.
Queue m_Queue
Queue of the chunks to be generated.
cEvent m_Event
Set when an item is added to the queue or the thread should terminate.
virtual void Execute(void) override
This function, overloaded by the descendants, is called in the new thread.
bool Initialize(cPluginInterface &a_PluginInterface, cChunkSink &a_ChunkSink, cIniFile &a_IniFile)
Read settings from the ini file and initialize in preperation for being started.
cChunkSink * m_ChunkSink
The destination where the generated chunks are sent.
cCriticalSection m_CS
CS protecting access to the queue.
EMCSBiome GetBiomeAt(int a_BlockX, int a_BlockZ)
Returns the biome at the specified coords.
void DoGenerate(cChunkCoords a_Coords)
Generates the specified chunk and sets it into the chunksink.
void GenerateBiomes(cChunkCoords a_Coords, cChunkDef::BiomeMap &a_BiomeMap)
Generates the biomes for the specified chunk (directly, not in a separate thread).
std::unique_ptr< cChunkGenerator > m_Generator
The actual chunk generator engine used.
void QueueGenerateChunk(cChunkCoords a_Coords, bool a_ForceRegeneration, cChunkCoordCallback *a_Callback=nullptr)
Queues the chunk for generation If a-ForceGenerate is set, the chunk is regenerated even if the data ...
cPluginInterface * m_PluginInterface
The plugin interface that may modify the generated chunks.
virtual ~cChunkGeneratorThread() override
The interface through which the plugins are called for their OnChunkGenerating / OnChunkGenerated hoo...
virtual void CallHookChunkGenerated(cChunkDesc &a_ChunkDesc)=0
Called after the chunk is generated, before it is handed to the chunk sink.
virtual void CallHookChunkGenerating(cChunkDesc &a_ChunkDesc)=0
Called when the chunk is about to be generated.
The interface through which the generated chunks are handed to the cWorld or whoever created us.
virtual bool HasChunkAnyClients(cChunkCoords a_Coords)=0
Called when the generator is overloaded to skip chunks that are no longer needed.
virtual bool IsChunkValid(cChunkCoords a_Coords)=0
Called just before the chunk generation is started, to verify that it hasn't been generated in the me...
virtual void OnChunkGenerated(cChunkDesc &a_ChunkDesc)=0
Called after the chunk has been generated The interface may store the chunk, send it over network,...
virtual bool IsChunkQueued(cChunkCoords a_Coords)=0
Called to check whether the specified chunk is in the queued state.
void VerifyHeightmap(void)
Verifies that the heightmap corresponds to blocktype contents; if not, asserts on that column.
Definition: ChunkDesc.cpp:652
static std::unique_ptr< cChunkGenerator > CreateFromIniFile(cIniFile &a_IniFile)
Creates and initializes the entire generator based on the settings in the INI file.
RAII for cCriticalSection - locks the CS on creation, unlocks on destruction.
void Unlock(void)
Temporary RAII unlock for a cCSLock.
void Wait(void)
Waits until the event has been set.
Definition: Event.cpp:23
void Set(void)
Sets the event - releases one thread that has been waiting in Wait().
Definition: Event.cpp:52
std::atomic< bool > m_ShouldTerminate
The overriden Execute() method should check this value periodically and terminate if this is true.
Definition: IsThread.h:45
void Stop(void)
Signals the thread to terminate and waits until it's finished.
Definition: IsThread.cpp:48