Cuberite
A lightweight, fast and extensible game server for Minecraft
WorldStorage.cpp
Go to the documentation of this file.
1 
2 // WorldStorage.cpp
3 
4 // Implements the cWorldStorage class representing the chunk loading / saving thread
5 
6 // To add a new storage schema, implement a cWSSchema descendant and add it to cWorldStorage::InitSchemas()
7 
8 #include "Globals.h"
9 #include "WorldStorage.h"
10 #include "WSSAnvil.h"
11 #include "../World.h"
12 #include "../Generating/ChunkGenerator.h"
13 #include "../Entities/Entity.h"
14 #include "../BlockEntities/BlockEntity.h"
15 
16 
17 
18 
21  public cWSSchema
22 {
23 public:
24  cWSSForgetful(cWorld * a_World) : cWSSchema(a_World) {}
25 
26 protected:
27  // cWSSchema overrides:
28  virtual bool LoadChunk(const cChunkCoords & a_Chunk) override {return false; }
29  virtual bool SaveChunk(const cChunkCoords & a_Chunk) override {return true; }
30  virtual const AString GetName(void) const override {return "forgetful"; }
31 } ;
32 
33 
34 
35 
36 
38 // cWorldStorage:
39 
41  Super("World Storage Executor"),
42  m_World(nullptr),
43  m_SaveSchema(nullptr)
44 {
45 }
46 
47 
48 
49 
50 
52 {
53  for (cWSSchemaList::iterator itr = m_Schemas.begin(); itr != m_Schemas.end(); ++itr)
54  {
55  delete *itr;
56  } // for itr - m_Schemas[]
57 }
58 
59 
60 
61 
62 
63 void cWorldStorage::Initialize(cWorld & a_World, const AString & a_StorageSchemaName, int a_StorageCompressionFactor)
64 {
65  m_World = &a_World;
66  m_StorageSchemaName = a_StorageSchemaName;
67  InitSchemas(a_StorageCompressionFactor);
68 }
69 
70 
71 
72 
73 
75 {
76  WaitForFinish();
77 }
78 
79 
80 
81 
82 
84 {
85  LOGD("Waiting for the world storage to finish saving");
86 
87  {
89  }
90 
91  // Wait for the saving to finish:
93 
94  // Wait for the thread to finish:
95  m_ShouldTerminate = true;
96  m_Event.Set(); // Wake up the thread if waiting
97  Super::Stop();
98  LOGD("World storage thread finished");
99 }
100 
101 
102 
103 
104 
106 {
108 }
109 
110 
111 
112 
113 
115 {
117 }
118 
119 
120 
121 
122 
124 {
125  return m_LoadQueue.Size();
126 }
127 
128 
129 
130 
131 
133 {
134  return m_SaveQueue.Size();
135 }
136 
137 
138 
139 
140 
141 void cWorldStorage::QueueLoadChunk(int a_ChunkX, int a_ChunkZ)
142 {
143  ASSERT((a_ChunkX > -0x08000000) && (a_ChunkX < 0x08000000));
144  ASSERT((a_ChunkZ > -0x08000000) && (a_ChunkZ < 0x08000000));
145  ASSERT(m_World->IsChunkQueued(a_ChunkX, a_ChunkZ));
146 
147  m_LoadQueue.EnqueueItem({ a_ChunkX, a_ChunkZ });
148  m_Event.Set();
149 }
150 
151 
152 
153 
154 
155 void cWorldStorage::QueueSaveChunk(int a_ChunkX, int a_ChunkZ)
156 {
157  ASSERT(m_World->IsChunkValid(a_ChunkX, a_ChunkZ));
158 
159  m_SaveQueue.EnqueueItem({ a_ChunkX, a_ChunkZ });
160  m_Event.Set();
161 }
162 
163 
164 
165 
166 
167 void cWorldStorage::InitSchemas(int a_StorageCompressionFactor)
168 {
169  // The first schema added is considered the default
170  m_Schemas.push_back(new cWSSAnvil (m_World, a_StorageCompressionFactor));
171  m_Schemas.push_back(new cWSSForgetful(m_World));
172  // Add new schemas here
173 
174  if (NoCaseCompare(m_StorageSchemaName, "default") == 0)
175  {
176  m_SaveSchema = m_Schemas.front();
177  return;
178  }
179  for (cWSSchemaList::iterator itr = m_Schemas.begin(); itr != m_Schemas.end(); ++itr)
180  {
181  if (NoCaseCompare((*itr)->GetName(), m_StorageSchemaName) == 0)
182  {
183  m_SaveSchema = *itr;
184  return;
185  }
186  } // for itr - m_Schemas[]
187 
188  // Unknown schema selected, let the admin know:
189  LOGWARNING("Unknown storage schema name \"%s\". Using default (\"%s\"). Available schemas:",
190  m_StorageSchemaName.c_str(), m_SaveSchema->GetName().c_str()
191  );
192  for (cWSSchemaList::iterator itr = m_Schemas.begin(); itr != m_Schemas.end(); ++itr)
193  {
194  LOGWARNING("\t\"%s\"", (*itr)->GetName().c_str());
195  }
196  m_SaveSchema = m_Schemas.front();
197 }
198 
199 
200 
201 
202 
204 {
205  while (!m_ShouldTerminate)
206  {
207  m_Event.Wait();
208  // Process both queues until they are empty again:
209  bool Success;
210  do
211  {
212  if (m_ShouldTerminate)
213  {
214  return;
215  }
216 
217  Success = LoadOneChunk();
218  Success |= SaveOneChunk();
219  } while (Success);
220  }
221 }
222 
223 
224 
225 
226 
228 {
229  // Dequeue an item, bail out if there's none left:
230  cChunkCoords ToLoad(0, 0);
231  bool ShouldLoad = m_LoadQueue.TryDequeueItem(ToLoad);
232  if (!ShouldLoad)
233  {
234  return false;
235  }
236 
237  // Load the chunk:
238  LoadChunk(ToLoad.m_ChunkX, ToLoad.m_ChunkZ);
239 
240  return true;
241 }
242 
243 
244 
245 
246 
248 {
249  // Dequeue one chunk to save:
250  cChunkCoords ToSave(0, 0);
251  bool ShouldSave = m_SaveQueue.TryDequeueItem(ToSave);
252  if (!ShouldSave)
253  {
254  return false;
255  }
256 
257  // Save the chunk, if it's valid:
258  if (m_World->IsChunkValid(ToSave.m_ChunkX, ToSave.m_ChunkZ))
259  {
260  m_World->MarkChunkSaving(ToSave.m_ChunkX, ToSave.m_ChunkZ);
261  if (m_SaveSchema->SaveChunk(cChunkCoords(ToSave.m_ChunkX, ToSave.m_ChunkZ)))
262  {
263  m_World->MarkChunkSaved(ToSave.m_ChunkX, ToSave.m_ChunkZ);
264  }
265  }
266 
267  return true;
268 }
269 
270 
271 
272 
273 
274 bool cWorldStorage::LoadChunk(int a_ChunkX, int a_ChunkZ)
275 {
276  ASSERT(m_World->IsChunkQueued(a_ChunkX, a_ChunkZ));
277 
278  cChunkCoords Coords(a_ChunkX, a_ChunkZ);
279 
280  // First try the schema that is used for saving
281  if (m_SaveSchema->LoadChunk(Coords))
282  {
283  return true;
284  }
285 
286  // If it didn't have the chunk, try all the other schemas:
287  for (cWSSchemaList::iterator itr = m_Schemas.begin(); itr != m_Schemas.end(); ++itr)
288  {
289  if (((*itr) != m_SaveSchema) && (*itr)->LoadChunk(Coords))
290  {
291  return true;
292  }
293  }
294 
295  // Notify the chunk owner that the chunk failed to load (sets cChunk::m_HasLoadFailed to true):
296  m_World->ChunkLoadFailed(a_ChunkX, a_ChunkZ);
297 
298  return false;
299 }
300 
301 
302 
303 
304 
#define ASSERT(x)
Definition: Globals.h:276
void LOGWARNING(std::string_view a_Format, const Args &... args)
Definition: LoggerSimple.h:67
#define LOGD
Definition: LoggerSimple.h:83
int NoCaseCompare(const AString &s1, const AString &s2)
Case-insensitive string comparison.
std::string AString
Definition: StringUtils.h:11
Wraps the chunk coords into a single structure.
Definition: ChunkDef.h:57
int m_ChunkZ
Definition: ChunkDef.h:60
int m_ChunkX
Definition: ChunkDef.h:59
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
size_t Size(void)
Returns the size at time of being called.
Definition: Queue.h:141
void EnqueueItem(ItemType a_Item)
Enqueues an item to the queue, may block if other threads are accessing the queue.
Definition: Queue.h:57
void Clear(void)
Removes all Items from the Queue, calling Delete on each of them.
Definition: Queue.h:128
void BlockTillEmpty(void)
Blocks until the queue is empty.
Definition: Queue.h:116
bool TryDequeueItem(ItemType &item)
Dequeues an item from the queue if any are present.
Definition: Queue.h:85
Definition: World.h:53
void ChunkLoadFailed(int a_ChunkX, int a_ChunkZ)
Marks the chunk as failed-to-load:
Definition: World.cpp:2551
void MarkChunkSaved(int a_ChunkX, int a_ChunkZ)
Definition: World.cpp:2168
bool IsChunkValid(int a_ChunkX, int a_ChunkZ) const
Returns true iff the chunk is present and valid.
Definition: World.cpp:2220
bool IsChunkQueued(int a_ChunkX, int a_ChunkZ) const
Returns true iff the chunk is in the loader / generator queue.
Definition: World.cpp:2211
void MarkChunkSaving(int a_ChunkX, int a_ChunkZ)
Definition: World.cpp:2159
Example storage schema - forgets all chunks.
virtual bool SaveChunk(const cChunkCoords &a_Chunk) override
cWSSForgetful(cWorld *a_World)
virtual bool LoadChunk(const cChunkCoords &a_Chunk) override
virtual const AString GetName(void) const override
bool LoadChunk(int a_ChunkX, int a_ChunkZ)
Loads the chunk specified; returns true on success, false on failure.
void WaitForFinish(void)
bool LoadOneChunk(void)
Loads one chunk from the queue (if any queued); returns true if there was a chunk in the queue to loa...
cWorld * m_World
Definition: WorldStorage.h:80
void Stop(void)
virtual ~cWorldStorage() override
void Initialize(cWorld &a_World, const AString &a_StorageSchemaName, int a_StorageCompressionFactor)
Initializes the storage schemas, ready to be started.
cEvent m_Event
Set when there's any addition to the queues.
Definition: WorldStorage.h:93
AString m_StorageSchemaName
Definition: WorldStorage.h:81
size_t GetSaveQueueLength(void)
size_t GetLoadQueueLength(void)
virtual void Execute(void) override
This function, overloaded by the descendants, is called in the new thread.
void WaitForSaveQueueEmpty(void)
bool SaveOneChunk(void)
Saves one chunk from the queue (if any queued); returns true if there was a chunk in the queue to sav...
void QueueLoadChunk(int a_ChunkX, int a_ChunkZ)
Queues a chunk to be loaded, asynchronously.
cQueue< cChunkCoords > m_LoadQueue
Definition: WorldStorage.h:83
void InitSchemas(int a_StorageCompressionFactor)
void QueueSaveChunk(int a_ChunkX, int a_ChunkZ)
Queues a chunk to be saved, asynchronously.
void WaitForLoadQueueEmpty(void)
cQueue< cChunkCoords > m_SaveQueue
Definition: WorldStorage.h:84
cWSSchema * m_SaveSchema
The one storage schema used for saving.
Definition: WorldStorage.h:90
cWSSchemaList m_Schemas
All the storage schemas (all used for loading)
Definition: WorldStorage.h:87
Implements the Anvil world storage schema.
Definition: WSSAnvil.h:28