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 
cWorldStorage::Initialize
void Initialize(cWorld &a_World, const AString &a_StorageSchemaName, int a_StorageCompressionFactor)
Initializes the storage schemas, ready to be started.
Definition: WorldStorage.cpp:63
WorldStorage.h
cEvent::Wait
void Wait(void)
Waits until the event has been set.
Definition: Event.cpp:23
cWSSForgetful::SaveChunk
virtual bool SaveChunk(const cChunkCoords &a_Chunk) override
Definition: WorldStorage.cpp:29
cWorldStorage::GetSaveQueueLength
size_t GetSaveQueueLength(void)
Definition: WorldStorage.cpp:132
cWorld::ChunkLoadFailed
void ChunkLoadFailed(int a_ChunkX, int a_ChunkZ)
Marks the chunk as failed-to-load:
Definition: World.cpp:2537
cWSSForgetful::cWSSForgetful
cWSSForgetful(cWorld *a_World)
Definition: WorldStorage.cpp:24
cWorld::IsChunkQueued
bool IsChunkQueued(int a_ChunkX, int a_ChunkZ) const
Returns true iff the chunk is in the loader / generator queue.
Definition: World.cpp:2197
cWorldStorage::WaitForFinish
void WaitForFinish(void)
Definition: WorldStorage.cpp:83
cIsThread
Definition: IsThread.h:23
cWorldStorage::~cWorldStorage
virtual ~cWorldStorage() override
Definition: WorldStorage.cpp:51
cWorldStorage::WaitForLoadQueueEmpty
void WaitForLoadQueueEmpty(void)
Definition: WorldStorage.cpp:105
cWorldStorage::QueueSaveChunk
void QueueSaveChunk(int a_ChunkX, int a_ChunkZ)
Queues a chunk to be saved, asynchronously.
Definition: WorldStorage.cpp:155
Globals.h
NoCaseCompare
int NoCaseCompare(const AString &s1, const AString &s2)
Case-insensitive string comparison.
Definition: StringUtils.cpp:304
cChunkCoords::m_ChunkX
int m_ChunkX
Definition: ChunkDef.h:58
WSSAnvil.h
cWorld::IsChunkValid
bool IsChunkValid(int a_ChunkX, int a_ChunkZ) const
Returns true iff the chunk is present and valid.
Definition: World.cpp:2206
ASSERT
#define ASSERT(x)
Definition: Globals.h:273
cWorldStorage::m_Schemas
cWSSchemaList m_Schemas
All the storage schemas (all used for loading)
Definition: WorldStorage.h:87
LOGD
#define LOGD
Definition: LoggerSimple.h:83
cWorldStorage::Stop
void Stop(void)
Definition: WorldStorage.cpp:74
cWSSForgetful
Example storage schema - forgets all chunks.
Definition: WorldStorage.cpp:20
cWorld
Definition: World.h:47
cQueue::Size
size_t Size(void)
Returns the size at time of being called.
Definition: Queue.h:141
cQueue::TryDequeueItem
bool TryDequeueItem(ItemType &item)
Dequeues an item from the queue if any are present.
Definition: Queue.h:85
cWorldStorage::InitSchemas
void InitSchemas(int a_StorageCompressionFactor)
Definition: WorldStorage.cpp:167
cWSSAnvil
Definition: WSSAnvil.h:49
cWorldStorage::Execute
virtual void Execute(void) override
This function, overloaded by the descendants, is called in the new thread.
Definition: WorldStorage.cpp:203
cWorldStorage::m_Event
cEvent m_Event
Set when there's any addition to the queues.
Definition: WorldStorage.h:93
cWorldStorage::LoadOneChunk
bool LoadOneChunk(void)
Loads one chunk from the queue (if any queued); returns true if there was a chunk in the queue to loa...
Definition: WorldStorage.cpp:227
cWorldStorage::m_SaveQueue
cQueue< cChunkCoords > m_SaveQueue
Definition: WorldStorage.h:84
cWorld::MarkChunkSaving
void MarkChunkSaving(int a_ChunkX, int a_ChunkZ)
Definition: World.cpp:2145
cWorldStorage::m_StorageSchemaName
AString m_StorageSchemaName
Definition: WorldStorage.h:81
LOGWARNING
void LOGWARNING(std::string_view a_Format, const Args &... args)
Definition: LoggerSimple.h:67
cEvent::Set
void Set(void)
Sets the event - releases one thread that has been waiting in Wait().
Definition: Event.cpp:52
cWorldStorage::m_World
cWorld * m_World
Definition: WorldStorage.h:80
cIsThread::Stop
void Stop(void)
Signals the thread to terminate and waits until it's finished.
Definition: IsThread.cpp:48
cQueue::EnqueueItem
void EnqueueItem(ItemType a_Item)
Enqueues an item to the queue, may block if other threads are accessing the queue.
Definition: Queue.h:57
cWorldStorage::m_LoadQueue
cQueue< cChunkCoords > m_LoadQueue
Definition: WorldStorage.h:83
cChunkCoords
Definition: ChunkDef.h:55
cWSSForgetful::GetName
virtual const AString GetName(void) const override
Definition: WorldStorage.cpp:30
cWorldStorage::WaitForSaveQueueEmpty
void WaitForSaveQueueEmpty(void)
Definition: WorldStorage.cpp:114
cQueue::BlockTillEmpty
void BlockTillEmpty(void)
Blocks until the queue is empty.
Definition: Queue.h:116
cWorldStorage::GetLoadQueueLength
size_t GetLoadQueueLength(void)
Definition: WorldStorage.cpp:123
cWorldStorage::SaveOneChunk
bool SaveOneChunk(void)
Saves one chunk from the queue (if any queued); returns true if there was a chunk in the queue to sav...
Definition: WorldStorage.cpp:247
cWorldStorage::QueueLoadChunk
void QueueLoadChunk(int a_ChunkX, int a_ChunkZ)
Queues a chunk to be loaded, asynchronously.
Definition: WorldStorage.cpp:141
AString
std::string AString
Definition: StringUtils.h:11
cQueue::Clear
void Clear(void)
Removes all Items from the Queue, calling Delete on each of them.
Definition: Queue.h:128
cIsThread::m_ShouldTerminate
std::atomic< bool > m_ShouldTerminate
The overriden Execute() method should check this value periodically and terminate if this is true.
Definition: IsThread.h:45
cChunkCoords::m_ChunkZ
int m_ChunkZ
Definition: ChunkDef.h:59
cWSSForgetful::LoadChunk
virtual bool LoadChunk(const cChunkCoords &a_Chunk) override
Definition: WorldStorage.cpp:28
cWorldStorage::LoadChunk
bool LoadChunk(int a_ChunkX, int a_ChunkZ)
Loads the chunk specified; returns true on success, false on failure.
Definition: WorldStorage.cpp:274
cWorldStorage::m_SaveSchema
cWSSchema * m_SaveSchema
The one storage schema used for saving.
Definition: WorldStorage.h:90
cWorld::MarkChunkSaved
void MarkChunkSaved(int a_ChunkX, int a_ChunkZ)
Definition: World.cpp:2154
cWorldStorage::cWorldStorage
cWorldStorage()
Definition: WorldStorage.cpp:40