Cuberite
A lightweight, fast and extensible game server for Minecraft
ChunkSender.cpp
Go to the documentation of this file.
1 
2 // ChunkSender.cpp
3 
4 // Interfaces to the cChunkSender class representing the thread that waits for chunks becoming ready (loaded / generated) and sends them to clients
5 
6 
7 
8 
9 
10 #include "Globals.h"
11 #include "ChunkSender.h"
12 #include "World.h"
15 #include "ClientHandle.h"
16 #include "Chunk.h"
17 
18 
19 
20 
21 
23 // cNotifyChunkSender:
24 
25 
28  public cChunkCoordCallback
29 {
30  virtual void Call(cChunkCoords a_Coords, bool a_IsSuccess) override
31  {
32  cChunkSender & ChunkSender = m_ChunkSender;
34  a_Coords.m_ChunkX, a_Coords.m_ChunkZ,
35  [&ChunkSender] (cChunk & a_Chunk) -> bool
36  {
37  ChunkSender.QueueSendChunkTo(a_Chunk.GetPosX(), a_Chunk.GetPosZ(), cChunkSender::E_CHUNK_PRIORITY_MIDHIGH, a_Chunk.GetAllClients());
38  return true;
39  }
40  );
41  }
42 
44 
46 
47 public:
48  cNotifyChunkSender(cChunkSender & a_ChunkSender, cWorld & a_World):
49  m_ChunkSender(a_ChunkSender),
50  m_World(a_World)
51  {
52  }
53 };
54 
55 
56 
57 
58 
60 // cChunkSender:
61 
63  super("ChunkSender"),
64  m_World(a_World)
65 {
66 }
67 
68 
69 
70 
71 
73 {
74  Stop();
75 }
76 
77 
78 
79 
80 
82 {
83  m_ShouldTerminate = true;
84  m_evtQueue.Set();
85  super::Stop();
86 }
87 
88 
89 
90 
91 
92 void cChunkSender::QueueSendChunkTo(int a_ChunkX, int a_ChunkZ, eChunkPriority a_Priority, cClientHandle * a_Client)
93 {
94  ASSERT(a_Client != nullptr);
95  {
96  cChunkCoords Chunk{a_ChunkX, a_ChunkZ};
97  cCSLock Lock(m_CS);
98  auto iter = m_ChunkInfo.find(Chunk);
99  if (iter != m_ChunkInfo.end())
100  {
101  auto & info = iter->second;
102  if (info.m_Priority > a_Priority)
103  {
104  m_SendChunks.push(sChunkQueue{a_Priority, Chunk});
105  info.m_Priority = a_Priority;
106  }
107  info.m_Clients.insert(a_Client);
108  }
109  else
110  {
111  m_SendChunks.push(sChunkQueue{a_Priority, Chunk});
112  auto info = sSendChunk{Chunk, a_Priority};
113  info.m_Clients.insert(a_Client);
114  m_ChunkInfo.emplace(Chunk, info);
115  }
116  }
117  m_evtQueue.Set();
118 }
119 
120 
121 
122 
123 
124 void cChunkSender::QueueSendChunkTo(int a_ChunkX, int a_ChunkZ, eChunkPriority a_Priority, cChunkClientHandles a_Clients)
125 {
126  {
127  cChunkCoords Chunk{a_ChunkX, a_ChunkZ};
128  cCSLock Lock(m_CS);
129  auto iter = m_ChunkInfo.find(Chunk);
130  if (iter != m_ChunkInfo.end())
131  {
132  auto & info = iter->second;
133  if (info.m_Priority > a_Priority)
134  {
135  m_SendChunks.push(sChunkQueue{a_Priority, Chunk});
136  info.m_Priority = a_Priority;
137  }
138  info.m_Clients.insert(a_Clients.begin(), a_Clients.end());
139  }
140  else
141  {
142  m_SendChunks.push(sChunkQueue{a_Priority, Chunk});
143  auto info = sSendChunk{Chunk, a_Priority};
144  info.m_Clients.insert(a_Clients.begin(), a_Clients.end());
145  m_ChunkInfo.emplace(Chunk, info);
146  }
147  }
148  m_evtQueue.Set();
149 }
150 
151 
152 
153 
154 
156 {
157  {
158  cCSLock Lock(m_CS);
159  for (auto && pair : m_ChunkInfo)
160  {
161  auto && clients = pair.second.m_Clients;
162  clients.erase(a_Client); // nop for sets that do not contain a_Client
163  }
164  }
165  m_evtQueue.Set();
166  m_evtRemoved.Wait(); // Wait for all remaining instances of a_Client to be processed (Execute() makes a copy of m_ChunkInfo)
167 }
168 
169 
170 
171 
172 
174 {
175  while (!m_ShouldTerminate)
176  {
177  m_evtQueue.Wait();
178 
179  {
180  cCSLock Lock(m_CS);
181  while (!m_SendChunks.empty())
182  {
183  // Take one from the queue:
184  auto Chunk = m_SendChunks.top().m_Chunk;
185  m_SendChunks.pop();
186  auto itr = m_ChunkInfo.find(Chunk);
187  if (itr == m_ChunkInfo.end())
188  {
189  continue;
190  }
191 
192  std::unordered_set<cClientHandle *> clients;
193  std::swap(itr->second.m_Clients, clients);
194  m_ChunkInfo.erase(itr);
195 
196  cCSUnlock Unlock(Lock);
197  SendChunk(Chunk.m_ChunkX, Chunk.m_ChunkZ, clients);
198  }
199  }
200 
201  m_evtRemoved.SetAll(); // Notify all waiting threads that all clients are processed and thus safe to destroy
202  } // while (!m_ShouldTerminate)
203 }
204 
205 
206 
207 
208 
209 void cChunkSender::SendChunk(int a_ChunkX, int a_ChunkZ, std::unordered_set<cClientHandle *> a_Clients)
210 {
211  // Ask the client if it still wants the chunk:
212  for (auto itr = a_Clients.begin(); itr != a_Clients.end();)
213  {
214  if (!(*itr)->WantsSendChunk(a_ChunkX, a_ChunkZ))
215  {
216  itr = a_Clients.erase(itr);
217  }
218  else
219  {
220  itr++;
221  }
222  }
223 
224  // If the chunk has no clients, no need to packetize it:
225  if (!m_World.HasChunkAnyClients(a_ChunkX, a_ChunkZ))
226  {
227  return;
228  }
229 
230  // If the chunk is not valid, do nothing - whoever needs it has queued it for loading / generating
231  if (!m_World.IsChunkValid(a_ChunkX, a_ChunkZ))
232  {
233  return;
234  }
235 
236  // If the chunk is not lighted, queue it for relighting and get notified when it's ready:
237  if (!m_World.IsChunkLighted(a_ChunkX, a_ChunkZ))
238  {
239  m_World.QueueLightChunk(a_ChunkX, a_ChunkZ, cpp14::make_unique<cNotifyChunkSender>(*this, m_World));
240  return;
241  }
242 
243  // Query and prepare chunk data:
244  if (!m_World.GetChunkData({a_ChunkX, a_ChunkZ}, *this))
245  {
246  return;
247  }
249 
250  for (const auto client : a_Clients)
251  {
252  // Send:
253  client->SendChunkData(a_ChunkX, a_ChunkZ, Data);
254 
255  // Send block-entity packets:
256  for (const auto & Pos : m_BlockEntities)
257  {
258  m_World.SendBlockEntity(Pos.x, Pos.y, Pos.z, *client);
259  } // for itr - m_Packets[]
260 
261  }
262  m_Data.Clear();
263  m_BlockEntities.clear();
264 
265  // TODO: Send entity spawn packets
266 }
267 
268 
269 
270 
271 
273 {
274  m_BlockEntities.push_back(a_Entity->GetPos());
275 }
276 
277 
278 
279 
280 
282 {
283  // Nothing needed yet, perhaps in the future when we save entities into chunks we'd like to send them upon load, too ;)
284 }
285 
286 
287 
288 
289 
291 {
292  for (size_t i = 0; i < ARRAYCOUNT(m_BiomeMap); i++)
293  {
294  if ((*a_BiomeMap)[i] < 255)
295  {
296  // Normal MC biome, copy as-is:
297  m_BiomeMap[i] = static_cast<unsigned char>((*a_BiomeMap)[i]);
298  }
299  else
300  {
301  // TODO: MCS-specific biome, need to map to some basic MC biome:
302  ASSERT(!"Unimplemented MCS-specific biome");
303  }
304  } // for i - m_BiomeMap[]
305 }
306 
307 
308 
309 
std::unordered_set< cClientHandle * > m_Clients
Definition: ChunkSender.h:102
void Set(void)
Sets the event - releases one thread that has been waiting in Wait().
Definition: Event.cpp:53
cCriticalSection m_CS
Definition: ChunkSender.h:113
std::atomic< bool > m_ShouldTerminate
The overriden Execute() method should check this value periodically and terminate if this is true...
Definition: IsThread.h:32
cChunkSender(cWorld &a_World)
Definition: ChunkSender.cpp:62
void SendChunk(int a_ChunkX, int a_ChunkZ, std::unordered_set< cClientHandle * > a_Clients)
Sends the specified chunk to all the specified clients.
bool DoWithChunk(int a_ChunkX, int a_ChunkZ, cChunkCallback a_Callback)
Calls the callback for the chunk specified, with ChunkMapCS locked.
Definition: World.cpp:1558
unsigned char m_BiomeMap[cChunkDef::Width *cChunkDef::Width]
Definition: ChunkSender.h:121
cEvent m_evtRemoved
Definition: ChunkSender.h:117
bool HasChunkAnyClients(int a_ChunkX, int a_ChunkZ) const
Definition: World.cpp:2410
virtual void Call(cChunkCoords a_Coords, bool a_IsSuccess) override
Called with the chunk&#39;s coords, and an optional operation status flag for operations that support it...
Definition: ChunkSender.cpp:30
Definition: Chunk.h:49
Used for sending chunks to specific clients.
Definition: ChunkSender.h:99
cWorld & m_World
Definition: ChunkSender.h:111
Vector3i GetPos() const
Definition: BlockEntity.h:104
void Stop(void)
Definition: ChunkSender.cpp:81
void Wait(void)
Waits until the event has been set.
Definition: Event.cpp:24
std::vector< Vector3i > m_BlockEntities
Definition: ChunkSender.h:122
Definition: World.h:65
virtual void BiomeData(const cChunkDef::BiomeMap *a_BiomeMap) override
cNotifyChunkSender(cChunkSender &a_ChunkSender, cWorld &a_World)
Definition: ChunkSender.cpp:48
std::priority_queue< sChunkQueue > m_SendChunks
Definition: ChunkSender.h:114
std::unordered_map< cChunkCoords, sSendChunk, cChunkCoordsHash > m_ChunkInfo
Definition: ChunkSender.h:115
Serializes one chunk&#39;s data to (possibly multiple) protocol versions.
Temporary RAII unlock for a cCSLock.
bool IsChunkLighted(int a_ChunkX, int a_ChunkZ)
Definition: World.cpp:2961
#define ASSERT(x)
Definition: Globals.h:335
virtual eDimension GetDimension(void) const override
Definition: World.h:151
void Stop(void)
Signals the thread to terminate and waits until it&#39;s finished.
Definition: IsThread.cpp:110
int m_ChunkZ
Definition: ChunkDef.h:60
bool IsChunkValid(int a_ChunkX, int a_ChunkZ) const
Returns true iff the chunk is present and valid.
Definition: World.cpp:2401
cEvent m_evtQueue
Definition: ChunkSender.h:116
bool GetChunkData(cChunkCoords a_Coords, cChunkDataCallback &a_Callback) const
Calls the callback with the chunk&#39;s data, if available (with ChunkCS locked).
Definition: World.cpp:2374
void RemoveClient(cClientHandle *a_Client)
Removes the a_Client from all waiting chunk send operations.
const_iterator begin() const
Definition: ChunkDef.h:115
int m_ChunkX
Definition: ChunkDef.h:59
virtual ~cChunkSender() override
Definition: ChunkSender.cpp:72
virtual void Entity(cEntity *a_Entity) override
Interface class used as a callback for operations that involve chunk coords.
Definition: ChunkDef.h:610
const_iterator end() const
Definition: ChunkDef.h:118
Definition: Entity.h:73
RAII for cCriticalSection - locks the CS on creation, unlocks on destruction.
void QueueLightChunk(int a_ChunkX, int a_ChunkZ, std::unique_ptr< cChunkCoordCallback > a_Callback={})
Queues a chunk for lighting; a_Callback is called after the chunk is lighted.
Definition: World.cpp:2952
Callback that can be used to notify chunk sender upon another chunkcoord notification.
Definition: ChunkSender.cpp:27
void SendBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cClientHandle &a_Client)
If there is a block entity at the specified coords, sends it to the client specified.
Definition: World.cpp:2251
virtual void BlockEntity(cBlockEntity *a_Entity) override
virtual void Execute(void) override
This is the main thread entrypoint.
cChunkSender & m_ChunkSender
Definition: ChunkSender.cpp:43
EMCSBiome BiomeMap[Width *Width]
The type used for any biomemap operations and storage inside Cuberite, using Cuberite biomes (need no...
Definition: ChunkDef.h:147
void QueueSendChunkTo(int a_ChunkX, int a_ChunkZ, eChunkPriority a_Priority, cClientHandle *a_Client)
Queues a chunk to be sent to a specific client.
Definition: ChunkSender.cpp:92
void Clear()
Clears all data.
Definition: ChunkData.cpp:317
#define ARRAYCOUNT(X)
Evaluates to the number of elements in an array (compile-time!)
Definition: Globals.h:290
void SetAll(void)
Sets the event - releases all threads that have been waiting in Wait().
Definition: Event.cpp:66
Non-owning view of a chunk&#39;s client handles.
Definition: ChunkDef.h:103