Cuberite
A lightweight, fast and extensible game server for Minecraft
ClientHandle.cpp
Go to the documentation of this file.
1 #include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
2 
3 #include "ClientHandle.h"
4 #include "BlockInfo.h"
5 #include "Server.h"
6 #include "World.h"
7 #include "Chunk.h"
8 #include "Entities/Pickup.h"
10 #include "Entities/Player.h"
11 #include "Entities/Minecart.h"
12 #include "Inventory.h"
17 #include "UI/InventoryWindow.h"
18 #include "UI/CraftingWindow.h"
19 #include "UI/Window.h"
20 #include "UI/AnvilWindow.h"
21 #include "UI/BeaconWindow.h"
22 #include "UI/EnchantingWindow.h"
23 #include "Item.h"
24 #include "Mobs/Monster.h"
25 #include "ChatColor.h"
26 #include "Items/ItemHandler.h"
27 #include "Blocks/BlockHandler.h"
28 #include "Blocks/BlockBed.h"
29 #include "Blocks/ChunkInterface.h"
31 
32 #include "Root.h"
33 
34 #include "Protocol/Authenticator.h"
35 #include "Protocol/Protocol.h"
36 #include "CompositeChat.h"
37 #include "Items/ItemSword.h"
38 
39 #include "mbedtls/md5.h"
40 
41 
42 
44 #define MAX_EXPLOSIONS_PER_TICK 20
45 
47 #define MAX_BLOCK_CHANGE_INTERACTIONS 20
48 
50 #define MAX_CHAT_MSG_LENGTH 1024
51 
53 #define MAX_CHUNKS_STREAMED_PER_TICK 4
54 
55 
56 
57 
58 
60 
61 
63 
65 
66 
67 
69 // cClientHandle:
70 
71 cClientHandle::cClientHandle(const AString & a_IPString, int a_ViewDistance) :
72  m_CurrentViewDistance(a_ViewDistance),
73  m_RequestedViewDistance(a_ViewDistance),
74  m_IPString(a_IPString),
75  m_Player(nullptr),
76  m_CachedSentChunk(std::numeric_limits<decltype(m_CachedSentChunk.m_ChunkX)>::max(), std::numeric_limits<decltype(m_CachedSentChunk.m_ChunkZ)>::max()),
77  m_ProxyConnection(false),
78  m_HasSentDC(false),
79  m_LastStreamedChunkX(std::numeric_limits<decltype(m_LastStreamedChunkX)>::max()), // bogus chunk coords to force streaming upon login
80  m_LastStreamedChunkZ(std::numeric_limits<decltype(m_LastStreamedChunkZ)>::max()),
81  m_TicksSinceLastPacket(0),
82  m_TimeSinceLastUnloadCheck(0),
83  m_Ping(1000),
84  m_PingID(1),
85  m_BlockDigAnimStage(-1),
86  m_BlockDigAnimSpeed(0),
87  m_BlockDigAnimPos(s_IllegalPosition),
88  m_HasStartedDigging(false),
89  m_LastDigBlockPos(s_IllegalPosition),
90  m_State(csConnected),
91  m_NumExplosionsThisTick(0),
92  m_NumBlockChangeInteractionsThisTick(0),
93  m_UniqueID(0),
94  m_HasSentPlayerChunk(false),
95  m_Locale("en_GB"),
96  m_LastPlacedSign(s_IllegalPosition),
97  m_ProtocolVersion(0)
98 {
99  s_ClientCount++; // Not protected by CS because clients are always constructed from the same thread
101 
102  LOGD("New ClientHandle created at %p", static_cast<void *>(this));
103 }
104 
105 
106 
107 
108 
110 {
111  ASSERT(m_State == csDestroyed); // Has Destroy() been called?
112 
113  LOGD("Deleting client \"%s\" at %p", GetUsername().c_str(), static_cast<void *>(this));
114 
115  LOGD("ClientHandle at %p deleted", static_cast<void *>(this));
116 }
117 
118 
119 
120 
121 
123 {
124  if (!SetState(csDestroyed))
125  {
126  // Already called
127  LOGD("%s: client %p, \"%s\" already destroyed, bailing out", __FUNCTION__, static_cast<void *>(this), m_Username.c_str());
128  return;
129  }
130 
131  LOGD("%s: destroying client %p, \"%s\" @ %s", __FUNCTION__, static_cast<void *>(this), m_Username.c_str(), m_IPString.c_str());
132 
133  {
135  m_Protocol.HandleOutgoingData(m_OutgoingData); // Finalise any encryption.
136  m_Link->Send(m_OutgoingData.data(), m_OutgoingData.size()); // Flush remaining data.
137  m_Link->Shutdown(); // Cleanly close the connection.
138  m_Link.reset(); // Release the strong reference cTCPLink holds to ourself.
139  }
140 }
141 
142 
143 
144 
145 
147  bool ShouldAppendChatPrefixes, const AString & a_ChatPrefixS,
148  const AString & m_Color1, const AString & m_Color2
149 )
150 {
151  if (ShouldAppendChatPrefixes)
152  {
153  return fmt::format(FMT_STRING("{}[{}] {}"), m_Color1, a_ChatPrefixS, m_Color2);
154  }
155  else
156  {
157  return m_Color1;
158  }
159 }
160 
161 
162 
163 
164 
165 AString cClientHandle::FormatMessageType(bool ShouldAppendChatPrefixes, eMessageType a_ChatPrefix, const AString & a_AdditionalData)
166 {
167  switch (a_ChatPrefix)
168  {
169  case mtCustom: return "";
170  case mtFailure: return FormatChatPrefix(ShouldAppendChatPrefixes, "INFO", cChatColor::Rose, cChatColor::White);
171  case mtInformation: return FormatChatPrefix(ShouldAppendChatPrefixes, "INFO", cChatColor::Yellow, cChatColor::White);
172  case mtSuccess: return FormatChatPrefix(ShouldAppendChatPrefixes, "INFO", cChatColor::Green, cChatColor::White);
173  case mtWarning: return FormatChatPrefix(ShouldAppendChatPrefixes, "WARN", cChatColor::Rose, cChatColor::White);
174  case mtFatal: return FormatChatPrefix(ShouldAppendChatPrefixes, "FATAL", cChatColor::Red, cChatColor::White);
175  case mtDeath: return FormatChatPrefix(ShouldAppendChatPrefixes, "DEATH", cChatColor::Gray, cChatColor::White);
176  case mtJoin: return FormatChatPrefix(ShouldAppendChatPrefixes, "JOIN", cChatColor::Yellow, cChatColor::White);
177  case mtLeave: return FormatChatPrefix(ShouldAppendChatPrefixes, "LEAVE", cChatColor::Yellow, cChatColor::White);
178  case mtPrivateMessage:
179  {
180  if (ShouldAppendChatPrefixes)
181  {
182  return fmt::format(FMT_STRING("{}[MSG: {}] {}{}"), cChatColor::LightBlue, a_AdditionalData, cChatColor::White, cChatColor::Italic);
183  }
184  else
185  {
186  return fmt::format(FMT_STRING("{}: {}"), a_AdditionalData, cChatColor::LightBlue);
187  }
188  }
189  case mtMaxPlusOne: break;
190  }
191  return "";
192 }
193 
194 
195 
196 
197 
199 {
200  // Online UUIDs are always version 4 (random)
201  // We use Version 3 (MD5 hash) UUIDs for the offline UUIDs
202  // This guarantees that they will never collide with an online UUID and can be distinguished.
203  // This is also consistent with the vanilla offline UUID scheme.
204 
205  return cUUID::GenerateVersion3("OfflinePlayer:" + a_Username);
206 }
207 
208 
209 
210 
211 
212 bool cClientHandle::IsUUIDOnline(const cUUID & a_UUID)
213 {
214  // Online UUIDs are always version 4 (random)
215  // We use Version 3 (MD5 hash) UUIDs for the offline UUIDs
216  // This guarantees that they will never collide with an online UUID and can be distinguished.
217  return (a_UUID.Version() == 4);
218 }
219 
220 
221 
222 
223 
224 void cClientHandle::ProxyInit(const AString & a_IPString, const cUUID & a_UUID)
225 {
226  this->SetIPString(a_IPString);
227  this->SetUUID(a_UUID);
228 
229  this->m_ProxyConnection = true;
230 }
231 
232 
233 
234 
235 
236 void cClientHandle::ProxyInit(const AString & a_IPString, const cUUID & a_UUID, const Json::Value & a_Properties)
237 {
238  this->SetProperties(a_Properties);
239  this->ProxyInit(a_IPString, a_UUID);
240 }
241 
242 
243 
244 
245 
247 {
248  // Process received network data:
249  decltype(m_IncomingData) IncomingData;
250  {
252 
253  // Bail out when nothing was received:
254  if (m_IncomingData.empty())
255  {
256  return;
257  }
258 
259  std::swap(IncomingData, m_IncomingData);
260  }
261 
262  try
263  {
264  m_Protocol.HandleIncomingData(*this, IncomingData);
265  }
266  catch (const std::exception & Oops)
267  {
268  Kick(Oops.what());
269  }
270 }
271 
272 
273 
274 
275 
277 {
278  decltype(m_OutgoingData) OutgoingData;
279  {
281 
282  // Bail out when there's nothing to send to avoid TCPLink::Send overhead:
283  if (m_OutgoingData.empty())
284  {
285  return;
286  }
287 
288  std::swap(OutgoingData, m_OutgoingData);
289  }
290 
291  // Due to cTCPLink's design of holding a strong pointer to ourself, we need to explicitly reset m_Link.
292  // This means we need to check it's not nullptr before trying to send, but also capture the link,
293  // to prevent it being reset between the null check and the Send:
294  if (auto Link = m_Link; Link != nullptr)
295  {
296  m_Protocol.HandleOutgoingData(OutgoingData);
297  Link->Send(OutgoingData.data(), OutgoingData.size());
298  }
299 }
300 
301 
302 
303 
304 
305 void cClientHandle::Kick(const AString & a_Reason)
306 {
307  if (m_State >= csAuthenticating) // Don't log pings
308  {
309  LOGINFO("Kicking player %s for \"%s\"", m_Username.c_str(), StripColorCodes(a_Reason).c_str());
310  }
311  SendDisconnect(a_Reason);
312 }
313 
314 
315 
316 
317 
319 {
320  if (!m_ProxyConnection && cRoot::Get()->GetServer()->OnlyAllowBungeeCord())
321  {
322  Kick("You can only connect to this server using a Proxy.");
323 
324  return false;
325  }
326 
327  cServer * Server = cRoot::Get()->GetServer();
328 
329  // Proxy Shared Secret Check (BungeeGuard)
330  const AString & ForwardSecret = Server->GetProxySharedSecret();
331  const bool AllowBungee = Server->ShouldAllowBungeeCord();
332  const bool RequireForwardSecret = AllowBungee && !ForwardSecret.empty();
333 
334  if (RequireForwardSecret)
335  {
336  for (auto & Node : GetProperties())
337  {
338  if (Node.get("name", "").asString() == "bungeeguard-token")
339  {
340  AString SentToken = Node.get("value", "").asString();
341 
342  if (ForwardSecret.compare(SentToken) == 0)
343  {
344  return true;
345  }
346 
347  break;
348  }
349  }
350 
351  Kick("Unable to authenticate.");
352  return false;
353  }
354  else if (m_ProxyConnection)
355  {
356  LOG("A player connected through a proxy without requiring a forwarding secret. If open to the internet, this is very insecure!");
357  }
358 
359  return true;
360 }
361 
362 
363 
364 
365 
366 void cClientHandle::Authenticate(AString && a_Name, const cUUID & a_UUID, Json::Value && a_Properties)
367 {
368  cCSLock Lock(m_CSState);
369  /*
370  LOGD("Processing authentication for client %s @ %s (%p), state = %d",
371  m_Username.c_str(), m_IPString.c_str(), static_cast<void *>(this), m_State.load()
372  );
373  //*/
374 
375  if (m_State != csAuthenticating)
376  {
377  return;
378  }
379 
380  ASSERT(m_Player == nullptr);
381 
382  if (!BungeeAuthenticate())
383  {
384  return;
385  }
386 
387  m_Username = std::move(a_Name);
388 
389  // Only assign UUID and properties if not already pre-assigned (BungeeCord sends those in the Handshake packet):
390  if (m_UUID.IsNil())
391  {
392  m_UUID = a_UUID;
393  }
394  if (m_Properties.empty())
395  {
396  m_Properties = std::move(a_Properties);
397  }
398 
399  // Send login success (if the protocol supports it):
400  m_Protocol->SendLoginSuccess();
401 
403  {
405  }
406  else
407  {
409  }
410 }
411 
412 
413 
414 
415 
417 {
418  // Serverside spawned player (so data are loaded).
419  std::unique_ptr<cPlayer> Player;
420 
421  try
422  {
423  Player = std::make_unique<cPlayer>(shared_from_this());
424  }
425  catch (const std::exception & Oops)
426  {
427  LOGWARNING("Player \"%s\" save or statistics file loading failed: %s", GetUsername().c_str(), Oops.what());
428  Kick("Contact an operator.\n\nYour player's save files could not be parsed.\nTo avoid data loss you are prevented from joining.");
429  return;
430  }
431 
432  m_Player = Player.get();
433 
434  /*
435  LOGD("Created a new cPlayer object at %p for client %s @ %s (%p)",
436  static_cast<void *>(m_Player),
437  m_Username.c_str(), m_IPString.c_str(), static_cast<void *>(this)
438  );
439  //*/
440 
442  if (World == nullptr)
443  {
445  }
446 
447  // Atomically increment player count (in server thread):
449 
450  if (!cRoot::Get()->GetPluginManager()->CallHookPlayerJoined(*m_Player))
451  {
452  cRoot::Get()->BroadcastChatJoin(fmt::format(FMT_STRING("{} has joined the game"), m_Username));
453  LOGINFO("Player %s has joined the game", m_Username);
454  }
455 
456  // TODO: this accesses the world spawn from the authenticator thread
457  // World spawn should be sent in OnAddedToWorld.
458  // Return a server login packet:
459  m_Protocol->SendLogin(*m_Player, *World);
460 
461  // Send the player's permission level.
462  // The key effect is to allow 1.9+ clients to open the command block UI.
464 
465  if (m_Player->GetKnownRecipes().empty())
466  {
467  SendInitRecipes(0);
468  }
469  else
470  {
471  for (const auto KnownRecipe : m_Player->GetKnownRecipes())
472  {
473  SendInitRecipes(KnownRecipe);
474  }
475  }
476 
477  // Send player list items:
478  SendPlayerListAddPlayer(*m_Player); // Add ourself
479  cRoot::Get()->BroadcastPlayerListsAddPlayer(*m_Player); // Add ourself to everyone else
480  cRoot::Get()->SendPlayerLists(m_Player); // Add everyone else to ourself
481 
482  // Send statistics:
484 
485  // Delay the first ping until the client "settles down"
486  // This should fix #889, "BadCast exception, cannot convert bit to fm" error in client
487  m_PingStartTime = std::chrono::steady_clock::now() + std::chrono::seconds(3); // Send the first KeepAlive packet in 3 seconds
488 
489  // Remove the client handle from the server, it will be ticked from its cPlayer object from now on:
491 
493  m_Player->Initialize(std::move(Player), *World);
494 
495  // LOGD("Client %s @ %s (%p) has been fully authenticated", m_Username.c_str(), m_IPString.c_str(), static_cast<void *>(this));
496 }
497 
498 
499 
500 
501 
503 {
504  ASSERT(m_Player != nullptr);
505 
506  int ChunkPosX = m_Player->GetChunkX();
507  int ChunkPosZ = m_Player->GetChunkZ();
508 
509  if ((m_LastStreamedChunkX == ChunkPosX) && (m_LastStreamedChunkZ == ChunkPosZ))
510  {
511  // All chunks are already loaded and the player has not moved, work is done:
512  return;
513  }
514 
515  // Player moved chunks and / or loading is not finished, reset to bogus (GH #4531):
516  m_LastStreamedChunkX = std::numeric_limits<decltype(m_LastStreamedChunkX)>::max();
517  m_LastStreamedChunkZ = std::numeric_limits<decltype(m_LastStreamedChunkZ)>::max();
518 
519  int StreamedChunks = 0;
520  Vector3d Position = m_Player->GetEyePosition();
521  Vector3d LookVector = m_Player->GetLookVector();
522 
523  // Get the look vector and normalize it.
524  LookVector.Normalize();
525 
526  // High priority: Load the chunks that are in the view-direction of the player (with a radius of 3)
527  for (int Range = 0; Range < m_CurrentViewDistance; Range++)
528  {
529  Vector3d Vector = Position + LookVector * cChunkDef::Width * Range;
530 
531  // Get the chunk from the x / z coords.
532  int RangeX, RangeZ = 0;
533  cChunkDef::BlockToChunk(FloorC(Vector.x), FloorC(Vector.z), RangeX, RangeZ);
534 
535  for (int X = 0; X < 7; X++)
536  {
537  for (int Z = 0; Z < 7; Z++)
538  {
539  int ChunkX = RangeX + ((X >= 4) ? (3 - X) : X);
540  int ChunkZ = RangeZ + ((Z >= 4) ? (3 - Z) : Z);
541  cChunkCoords Coords(ChunkX, ChunkZ);
542 
543  // Checks if the chunk is in distance
544  if ((Diff(ChunkX, ChunkPosX) > m_CurrentViewDistance) || (Diff(ChunkZ, ChunkPosZ) > m_CurrentViewDistance))
545  {
546  continue;
547  }
548 
549  // If the chunk already loading / loaded -> skip
550  {
551  cCSLock Lock(m_CSChunkLists);
552  if (
553  (m_ChunksToSend.find(Coords) != m_ChunksToSend.end()) ||
554  (m_LoadedChunks.find(Coords) != m_LoadedChunks.end())
555  )
556  {
557  continue;
558  }
559  }
560 
561  // Unloaded chunk found -> Send it to the client.
563 
564  if (++StreamedChunks == MAX_CHUNKS_STREAMED_PER_TICK)
565  {
566  return;
567  }
568  }
569  }
570  }
571 
572  // Low priority: Add all chunks that are in range. (From the center out to the edge)
573  for (int d = 0; d <= m_CurrentViewDistance; ++d) // cycle through (square) distance, from nearest to furthest
574  {
575  const auto StreamIfUnloaded = [this](const cChunkCoords Chunk)
576  {
577  // If the chunk already loading / loaded -> skip
578  {
579  cCSLock Lock(m_CSChunkLists);
580  if (
581  (m_ChunksToSend.find(Chunk) != m_ChunksToSend.end()) ||
582  (m_LoadedChunks.find(Chunk) != m_LoadedChunks.end())
583  )
584  {
585  return false;
586  }
587  }
588 
589  // Unloaded chunk found -> Send it to the client.
590  StreamChunk(Chunk.m_ChunkX, Chunk.m_ChunkZ, cChunkSender::Priority::Low);
591  return true;
592  };
593 
594  // For each distance, send the first unloaded chunk in a hollow square centered around current position:
595  for (int i = -d; i <= d; ++i)
596  {
597  if (StreamIfUnloaded({ ChunkPosX + d, ChunkPosZ + i }) || StreamIfUnloaded({ ChunkPosX - d, ChunkPosZ + i }))
598  {
599  if (++StreamedChunks == MAX_CHUNKS_STREAMED_PER_TICK)
600  {
601  return;
602  }
603  }
604  }
605  for (int i = -d + 1; i < d; ++i)
606  {
607  if (StreamIfUnloaded({ ChunkPosX + i, ChunkPosZ + d }) || StreamIfUnloaded({ ChunkPosX + i, ChunkPosZ - d }))
608  {
609  if (++StreamedChunks == MAX_CHUNKS_STREAMED_PER_TICK)
610  {
611  return;
612  }
613  }
614  }
615  }
616 
617  // All chunks are loaded -> Sets the last loaded chunk coordinates to current coordinates
618  m_LastStreamedChunkX = ChunkPosX;
619  m_LastStreamedChunkZ = ChunkPosZ;
620 }
621 
622 
623 
624 
625 
627 {
628  int ChunkPosX = FAST_FLOOR_DIV(static_cast<int>(m_Player->GetPosX()), cChunkDef::Width);
629  int ChunkPosZ = FAST_FLOOR_DIV(static_cast<int>(m_Player->GetPosZ()), cChunkDef::Width);
630 
631  cChunkCoordsList ChunksToRemove;
632  {
633  cCSLock Lock(m_CSChunkLists);
634  for (auto itr = m_LoadedChunks.begin(); itr != m_LoadedChunks.end();)
635  {
636  const auto DiffX = Diff((*itr).m_ChunkX, ChunkPosX);
637  const auto DiffZ = Diff((*itr).m_ChunkZ, ChunkPosZ);
638  if ((DiffX > m_CurrentViewDistance) || (DiffZ > m_CurrentViewDistance))
639  {
640  ChunksToRemove.push_back(*itr);
641  itr = m_LoadedChunks.erase(itr);
642  }
643  else
644  {
645  ++itr;
646  }
647  }
648 
649  for (auto itr = m_ChunksToSend.begin(); itr != m_ChunksToSend.end();)
650  {
651  const auto DiffX = Diff((*itr).m_ChunkX, ChunkPosX);
652  const auto DiffZ = Diff((*itr).m_ChunkZ, ChunkPosZ);
653  if ((DiffX > m_CurrentViewDistance) || (DiffZ > m_CurrentViewDistance))
654  {
655  itr = m_ChunksToSend.erase(itr);
656  }
657  else
658  {
659  ++itr;
660  }
661  }
662  }
663 
664  for (cChunkCoordsList::iterator itr = ChunksToRemove.begin(); itr != ChunksToRemove.end(); ++itr)
665  {
666  m_Player->GetWorld()->RemoveChunkClient(itr->m_ChunkX, itr->m_ChunkZ, this);
667  SendUnloadChunk(itr->m_ChunkX, itr->m_ChunkZ);
668  }
669 }
670 
671 
672 
673 
674 
675 void cClientHandle::StreamChunk(int a_ChunkX, int a_ChunkZ, cChunkSender::Priority a_Priority)
676 {
677  cWorld * World = m_Player->GetWorld();
678  ASSERT(World != nullptr);
679 
680  if (World->AddChunkClient(a_ChunkX, a_ChunkZ, this))
681  {
682  {
683  cCSLock Lock(m_CSChunkLists);
684  m_LoadedChunks.emplace(a_ChunkX, a_ChunkZ);
685  m_ChunksToSend.emplace(a_ChunkX, a_ChunkZ);
686  }
687  World->SendChunkTo(a_ChunkX, a_ChunkZ, a_Priority, this);
688  }
689 }
690 
691 
692 
693 
694 
695 void cClientHandle::HandleAnimation(const bool a_SwingMainHand)
696 {
697  if (cPluginManager::Get()->CallHookPlayerAnimation(*m_Player, a_SwingMainHand ? 0 : 1))
698  {
699  // Plugin disagrees, bail out:
700  return;
701  }
702 
704 }
705 
706 
707 
708 
709 
710 void cClientHandle::HandleNPCTrade(int a_SlotNum)
711 {
712  // TODO
713  LOGWARNING("%s: Not implemented yet", __FUNCTION__);
714 }
715 
716 
717 
718 
719 
721 {
723 }
724 
725 
726 
727 
728 
730 {
731  /* TODO: unused function, handles Legacy Server List Ping
732  http://wiki.vg/Protocol#Legacy_Server_List_Ping suggests that servers SHOULD handle this packet */
733 
734  // Somebody tries to retrieve information about the server
735  const cServer & Server = *cRoot::Get()->GetServer();
736 
737  auto Reply = fmt::format(FMT_STRING("{}{}{}{}{}"),
740  Server.GetMaxPlayers()
741  );
742  Kick(Reply);
743 }
744 
745 
746 
747 
748 
750 {
751  {
752  cCSLock lock(m_CSState);
753  if (m_State != csConnected)
754  {
755  /*
756  LOGD("Client %s @ %s (%p, state %d) has disconnected before logging in, bailing out of login",
757  a_Username.c_str(), m_IPString.c_str(), static_cast<void *>(this), m_State.load()
758  );
759  //*/
760  return false;
761  }
762 
763  // LOGD("Handling login for client %s @ %s (%p), state = %d", a_Username.c_str(), m_IPString.c_str(), static_cast<void *>(this), m_State.load());
764 
765  // Let the plugins know about this event, they may refuse the player:
766  if (cRoot::Get()->GetPluginManager()->CallHookLogin(*this, m_ProtocolVersion, GetUsername()))
767  {
768  SendDisconnect("Login Rejected!");
769  return false;
770  }
771 
773  } // lock(m_CSState)
774 
775  // Schedule for authentication; until then, let the player wait (but do not block):
777  return true;
778 }
779 
780 
781 
782 
783 
784 void cClientHandle::HandleCreativeInventory(Int16 a_SlotNum, const cItem & a_HeldItem, eClickAction a_ClickAction)
785 {
786  // This is for creative Inventory changes
788  {
789  LOGWARNING("Got a CreativeInventoryAction packet from user \"%s\" while not in creative mode. Ignoring.", m_Username.c_str());
790  return;
791  }
793  {
794  LOGWARNING("Got a CreativeInventoryAction packet from user \"%s\" while not in the inventory window. Ignoring.", m_Username.c_str());
795  return;
796  }
797 
798  m_Player->GetWindow()->Clicked(*m_Player, 0, a_SlotNum, a_ClickAction, a_HeldItem);
799 }
800 
801 
802 
803 
804 
805 void cClientHandle::HandleCrouch(const bool a_IsCrouching)
806 {
807  m_Player->SetCrouch(a_IsCrouching);
808 }
809 
810 
811 
812 
813 
814 void cClientHandle::HandleEnchantItem(UInt8 a_WindowID, UInt8 a_Enchantment)
815 {
816  if (a_Enchantment > 2)
817  {
818  LOGD("Player \"%s\" tried to select an invalid enchantment - hacked client?", m_Username.c_str());
819  return;
820  }
821 
822  // Bail out if something's wrong with the window:
823  if (
824  (m_Player->GetWindow() == nullptr) ||
825  (m_Player->GetWindow()->GetWindowID() != a_WindowID) ||
827  )
828  {
829  LOGD("Player \"%s\" tried to enchant without a valid window - hacked client?", m_Username.c_str());
830  return;
831  }
832 
833  cEnchantingWindow * Window = static_cast<cEnchantingWindow *>(m_Player->GetWindow());
834  const auto BaseEnchantmentLevel = Window->GetProperty(a_Enchantment);
835 
836  // Survival players must be checked they can afford enchantment and have lapis removed:
838  {
839  const auto XpRequired = m_Player->XpForLevel(BaseEnchantmentLevel);
840  auto LapisStack = *Window->m_SlotArea->GetSlot(1, *m_Player); // A copy of the lapis stack.
841  const auto LapisRequired = a_Enchantment + 1;
842 
843  // Only allow enchantment if the player has sufficient levels and lapis to enchant:
844  if ((m_Player->GetCurrentXp() >= XpRequired) && (LapisStack.m_ItemCount >= LapisRequired))
845  {
846  // We need to reduce the player's level by the number of lapis required.
847  // However we need to keep the resulting percentage filled the same.
848 
849  const auto TargetLevel = m_Player->GetXpLevel() - LapisRequired;
850  const auto CurrentFillPercent = m_Player->GetXpPercentage();
851 
852  // The experience to remove in order to reach the start (0% fill) of the target level.
853  const auto DeltaForLevel = -m_Player->GetCurrentXp() + m_Player->XpForLevel(TargetLevel);
854 
855  // The experience to add to get the same fill percent.
856  const auto DeltaForPercent = CurrentFillPercent * (m_Player->XpForLevel(TargetLevel + 1) - m_Player->XpForLevel(TargetLevel));
857 
858  // Apply the experience delta, rounded for greater accuracy:
859  m_Player->DeltaExperience(static_cast<int>(std::lround(DeltaForLevel + DeltaForPercent)));
860 
861  // Now reduce the lapis in our stack and send it back:
862  LapisStack.AddCount(static_cast<char>(-LapisRequired));
863  Window->m_SlotArea->SetSlot(1, *m_Player, LapisStack);
864  }
865  else
866  {
867  // Not creative and can't afford enchantment, so exit:
868  LOGD("Player \"%s\" selected unavailable enchantment - hacked client?", m_Username.c_str());
869  return;
870  }
871  }
872 
873  // The enchanted item corresponding to our chosen option (top, middle, bottom).
874  cItem EnchantedItem = Window->m_SlotArea->SelectEnchantedOption(a_Enchantment);
875 
876  // Set the item slot to our new enchanted item:
877  Window->m_SlotArea->SetSlot(0, *m_Player, EnchantedItem);
879 }
880 
881 
882 
883 
884 
885 void cClientHandle::HandlePlayerAbilities(bool a_IsFlying, float FlyingSpeed, float WalkingSpeed)
886 {
887  UNUSED(FlyingSpeed); // Ignore the client values for these
888  UNUSED(WalkingSpeed);
889 
890  m_Player->SetFlying(a_IsFlying);
891 }
892 
893 
894 
895 
896 
898 {
899  if (a_Channel == "REGISTER")
900  {
901  if (HasPluginChannel(a_Channel))
902  {
903  SendPluginMessage("UNREGISTER", a_Channel);
904  return; // Can't register again if already taken - kinda defeats the point of plugin messaging!
905  }
906 
908  }
909  else if (a_Channel == "UNREGISTER")
910  {
912  }
913  else if (a_Channel == "FML|HS")
914  {
915  m_ForgeHandshake.DataReceived(*this, a_Message);
916  }
917  else if (!HasPluginChannel(a_Channel))
918  {
919  // Ignore if client sent something but didn't register the channel first
920  LOGD("Player %s sent a plugin message on channel \"%s\", but didn't REGISTER it first", GetUsername().c_str(), a_Channel.c_str());
921  SendPluginMessage("UNREGISTER", a_Channel);
922  return;
923  }
924 
925  cPluginManager::Get()->CallHookPluginMessage(*this, a_Channel, a_Message);
926 }
927 
928 
929 
930 
931 
933 {
934  // Break the string on each NUL character.
935  // Note that StringSplit() doesn't work on this because NUL is a special char - string terminator
936  size_t len = a_PluginChannels.size();
937  size_t first = 0;
938  AStringVector res;
939  for (size_t i = 0; i < len; i++)
940  {
941  if (a_PluginChannels[i] != std::byte(0))
942  {
943  continue;
944  }
945  if (i > first)
946  {
947  const auto Part = a_PluginChannels.substr(first, i - first);
948  res.emplace_back(reinterpret_cast<const char *>(Part.data()), Part.size());
949  }
950  first = i + 1;
951  } // for i - a_PluginChannels[]
952  if (first < len)
953  {
954  const auto Part = a_PluginChannels.substr(first, len - first);
955  res.emplace_back(reinterpret_cast<const char *>(Part.data()), Part.size());
956  }
957  return res;
958 }
959 
960 
961 
962 
963 
965 {
966  for (AStringVector::const_iterator itr = a_ChannelList.begin(), end = a_ChannelList.end(); itr != end; ++itr)
967  {
968  m_PluginChannels.insert(*itr);
969  } // for itr - a_ChannelList[]
970 }
971 
972 
973 
974 
975 
977 {
978  for (AStringVector::const_iterator itr = a_ChannelList.begin(), end = a_ChannelList.end(); itr != end; ++itr)
979  {
980  m_PluginChannels.erase(*itr);
981  } // for itr - a_ChannelList[]
982 }
983 
984 
985 
986 
987 
988 void cClientHandle::HandleBeaconSelection(unsigned a_PrimaryEffect, unsigned a_SecondaryEffect)
989 {
990  cWindow * Window = m_Player->GetWindow();
991  if ((Window == nullptr) || (Window->GetWindowType() != cWindow::wtBeacon))
992  {
993  return;
994  }
995  cBeaconWindow * BeaconWindow = static_cast<cBeaconWindow *>(Window);
996 
997  if (Window->GetSlot(*m_Player, 0)->IsEmpty())
998  {
999  return;
1000  }
1001 
1003  if (a_PrimaryEffect <= static_cast<int>(cEntityEffect::effSaturation))
1004  {
1005  PrimaryEffect = static_cast<cEntityEffect::eType>(a_PrimaryEffect);
1006  }
1008  if (a_SecondaryEffect <= static_cast<int>(cEntityEffect::effSaturation))
1009  {
1010  SecondaryEffect = static_cast<cEntityEffect::eType>(a_SecondaryEffect);
1011  }
1012 
1013  Window->SetSlot(*m_Player, 0, cItem());
1014  BeaconWindow->GetBeaconEntity()->SetPrimaryEffect(PrimaryEffect);
1015 
1016  // Valid effect check. Vanilla don't check this, but we do it :)
1017  if (
1018  (SecondaryEffect == cEntityEffect::effNoEffect) ||
1019  (SecondaryEffect == cEntityEffect::effRegeneration) ||
1020  (SecondaryEffect == BeaconWindow->GetBeaconEntity()->GetPrimaryEffect())
1021  )
1022  {
1023  BeaconWindow->GetBeaconEntity()->SetSecondaryEffect(SecondaryEffect);
1024  }
1025  else
1026  {
1028  }
1029 
1030  m_Player->CloseWindow(true);
1031 }
1032 
1033 
1034 
1035 
1036 
1037 void cClientHandle::HandleCommandBlockBlockChange(Vector3i a_BlockPos, const AString & a_NewCommand)
1038 {
1039  if (a_NewCommand.empty())
1040  {
1041  LOGD("Player \"%s\" send an empty command block string - hacked client?", m_Username.c_str());
1042  return;
1043  }
1044 
1045  if ((m_Player == nullptr) || !m_Player->HasPermission("cuberite.commandblock.set"))
1046  {
1047  SendChat("You cannot edit command blocks on this server", mtFailure);
1048  return;
1049  }
1050 
1051  cWorld * World = m_Player->GetWorld();
1052  if (World->AreCommandBlocksEnabled())
1053  {
1054  World->SetCommandBlockCommand(a_BlockPos, a_NewCommand);
1055  SendChat("Successfully set command block command", mtSuccess);
1056  }
1057  else
1058  {
1059  SendChat("Command blocks are not enabled on this server", mtFailure);
1060  }
1061 }
1062 
1063 
1064 
1065 
1066 
1067 void cClientHandle::HandleCommandBlockEntityChange(UInt32 a_EntityID, const AString & a_NewCommand)
1068 {
1069  // TODO
1070  LOGWARNING("%s: Not implemented yet", __FUNCTION__);
1071 }
1072 
1073 
1074 
1075 
1076 
1078 {
1079  if ((m_Player->GetWindow() == nullptr) || (m_Player->GetWindow()->GetWindowType() != cWindow::wtAnvil))
1080  {
1081  return;
1082  }
1083 
1084  if (a_ItemName.length() <= 30)
1085  {
1086  static_cast<cAnvilWindow *>(m_Player->GetWindow())->SetRepairedItemName(a_ItemName, m_Player);
1087  }
1088 }
1089 
1090 
1091 
1092 
1093 
1094 void cClientHandle::HandleLeftClick(Vector3i a_BlockPos, eBlockFace a_BlockFace, UInt8 a_Status)
1095 {
1096  FLOGD("HandleLeftClick: {0}; Face: {1}; Stat: {2}",
1097  a_BlockPos, a_BlockFace, a_Status
1098  );
1099 
1101 
1103  {
1104  Kick("Too many blocks were destroyed per unit time - hacked client?");
1105  return;
1106  }
1107 
1108  if ((a_Status == DIG_STATUS_STARTED) || (a_Status == DIG_STATUS_FINISHED))
1109  {
1110  if (a_BlockFace == BLOCK_FACE_NONE)
1111  {
1112  return;
1113  }
1114 
1115  /* Check for clickthrough-blocks:
1116  When the user breaks a fire block, the client send the wrong block location.
1117  We must find the right block with the face direction. */
1118  if (
1119  const auto InterferingPosition = AddFaceDirection(a_BlockPos, a_BlockFace);
1120  cChunkDef::IsValidHeight(InterferingPosition) && cBlockInfo::IsClickedThrough(m_Player->GetWorld()->GetBlock(InterferingPosition))
1121  )
1122  {
1123  a_BlockPos = InterferingPosition;
1124  }
1125 
1126  if (!IsWithinReach(a_BlockPos))
1127  {
1128  m_Player->SendBlocksAround(a_BlockPos, 2);
1129  return;
1130  }
1131  }
1132 
1133  cPluginManager * PlgMgr = cRoot::Get()->GetPluginManager();
1134  if (m_Player->IsFrozen() || PlgMgr->CallHookPlayerLeftClick(*m_Player, a_BlockPos, a_BlockFace, static_cast<char>(a_Status)))
1135  {
1136  // A plugin doesn't agree with the action, replace the block on the client and quit:
1137  m_Player->SendBlocksAround(a_BlockPos, 2);
1138  SendPlayerPosition(); // Prevents the player from falling through the block that was temporarily broken client side.
1139  return;
1140  }
1141 
1142  switch (a_Status)
1143  {
1144  case DIG_STATUS_DROP_HELD: // Drop held item
1145  {
1146  if (PlgMgr->CallHookPlayerTossingItem(*m_Player))
1147  {
1148  // A plugin doesn't agree with the tossing. The plugin itself is responsible for handling the consequences (possible inventory mismatch)
1149  return;
1150  }
1151 
1153  return;
1154  }
1155 
1156  case DIG_STATUS_SHOOT_EAT:
1157  {
1158  auto & ItemHandler = m_Player->GetEquippedItem().GetHandler();
1159  if (ItemHandler.IsFood() || ItemHandler.IsDrinkable(m_Player->GetEquippedItem().m_ItemDamage))
1160  {
1161  m_Player->AbortEating();
1162  return;
1163  }
1164  else
1165  {
1166  if (PlgMgr->CallHookPlayerShooting(*m_Player))
1167  {
1168  // A plugin doesn't agree with the action. The plugin itself is responsible for handling the consequences (possible inventory mismatch)
1169  return;
1170  }
1171  // When bow is in off-hand / shield slot
1173  {
1174  m_Player->GetInventory().GetShieldSlot().GetHandler().OnItemShoot(m_Player, a_BlockPos, a_BlockFace);
1175  }
1176  else
1177  {
1178  ItemHandler.OnItemShoot(m_Player, a_BlockPos, a_BlockFace);
1179  }
1180  }
1181  return;
1182  }
1183 
1184  case DIG_STATUS_STARTED:
1185  {
1186  HandleBlockDigStarted(a_BlockPos, a_BlockFace);
1187  return;
1188  }
1189 
1190  case DIG_STATUS_FINISHED:
1191  {
1192  HandleBlockDigFinished(a_BlockPos, a_BlockFace);
1193  return;
1194  }
1195 
1196  case DIG_STATUS_CANCELLED:
1197  {
1198  // Block breaking cancelled by player
1200  return;
1201  }
1202 
1203  case DIG_STATUS_DROP_STACK:
1204  {
1205  if (PlgMgr->CallHookPlayerTossingItem(*m_Player))
1206  {
1207  // A plugin doesn't agree with the tossing. The plugin itself is responsible for handling the consequences (possible inventory mismatch)
1208  return;
1209  }
1210  m_Player->TossEquippedItem(64); // Toss entire slot - if there aren't enough items, the maximum will be ejected
1211  return;
1212  }
1213 
1215  {
1216 
1217  cItem EquippedItem = m_Player->GetEquippedItem();
1218  cItem OffhandItem = m_Player->GetOffHandEquipedItem();
1219 
1220  cInventory & Inventory = m_Player->GetInventory();
1221  Inventory.SetShieldSlot(EquippedItem);
1222  Inventory.SetEquippedItem(OffhandItem);
1223 
1224  return;
1225  }
1226 
1227  default:
1228  {
1229  ASSERT(!"Unhandled DIG_STATUS");
1230  return;
1231  }
1232  } // switch (a_Status)
1233 }
1234 
1235 
1236 
1237 
1238 
1240 {
1242  {
1243  // Players in adventure mode can't destroy blocks
1244  return;
1245  }
1246 
1247  if (
1248  m_HasStartedDigging && (a_BlockPos == m_LastDigBlockPos)
1249  )
1250  {
1251  // It is a duplicate packet, drop it right away
1252  return;
1253  }
1254 
1255  BLOCKTYPE DiggingBlock;
1256  NIBBLETYPE DiggingMeta;
1257  m_Player->GetWorld()->GetBlockTypeMeta(a_BlockPos, DiggingBlock, DiggingMeta);
1258 
1259  if (
1262  (DiggingBlock != E_BLOCK_FIRE)
1263  )
1264  {
1265  // Players can't destroy blocks with a sword in the hand.
1266  return;
1267  }
1268 
1269  // Set the last digging coords to the block being dug, so that they can be checked in DIG_FINISHED to avoid dig / aim bug in the client:
1270  m_HasStartedDigging = true;
1271  m_LastDigBlockPos = a_BlockPos;
1272 
1273  if (
1274  m_Player->IsGameModeCreative() || // In creative mode, digging is done immediately
1275  m_Player->CanInstantlyMine(DiggingBlock) // Sometimes the player is fast enough to instantly mine
1276  )
1277  {
1278  // Immediately done:
1279  m_BreakProgress = 1;
1280 
1281  HandleBlockDigFinished(a_BlockPos, a_BlockFace);
1282  return;
1283  }
1284 
1285  m_BreakProgress = 0;
1286 
1287  // Start dig animation
1288  // TODO: calculate real animation speed
1289  // TODO: Send animation packets even without receiving any other packets
1290  m_BlockDigAnimSpeed = 10;
1291  m_BlockDigAnimPos = a_BlockPos;
1292  m_BlockDigAnimStage = 0;
1294 
1295  cWorld * World = m_Player->GetWorld();
1296  cChunkInterface ChunkInterface(World->GetChunkMap());
1297  cBlockHandler::For(DiggingBlock).OnDigging(ChunkInterface, *World, *m_Player, a_BlockPos);
1298 
1300 }
1301 
1302 
1303 
1304 
1305 
1307 {
1308  if (
1309  !m_HasStartedDigging || // Hasn't received the DIG_STARTED packet
1310  (m_LastDigBlockPos != a_BlockPos) // DIG_STARTED has had different pos
1311  )
1312  {
1313  FLOGD("Prevented a dig / aim bug in the client (finish {0} vs start {1}, HSD: {2})",
1314  a_BlockPos,
1316  (m_HasStartedDigging ? "True" : "False")
1317  );
1318  return;
1319  }
1320 
1322 
1323  BLOCKTYPE DugBlock;
1324  NIBBLETYPE DugMeta;
1325  m_Player->GetWorld()->GetBlockTypeMeta(a_BlockPos, DugBlock, DugMeta);
1326 
1327  if (!m_Player->IsGameModeCreative())
1328  {
1330 
1331  // Check for very fast tools. Maybe instead of FASTBREAK_PERCENTAGE we should check we are within x multiplied by the progress per tick
1333  {
1334  LOGD("Break progress of player %s was less than expected: %f < %f\n", m_Player->GetName().c_str(), m_BreakProgress * 100, FASTBREAK_PERCENTAGE * 100);
1335  // AntiFastBreak doesn't agree with the breaking. Bail out. Send the block back to the client, so that it knows:
1336  m_Player->SendBlocksAround(a_BlockPos, 2);
1337  SendPlayerPosition(); // Prevents the player from falling through the block that was temporarily broken client side.
1338  m_Player->SendMessage("FastBreak?"); // TODO Anticheat hook
1339  return;
1340  }
1341  }
1342 
1343  cWorld * World = m_Player->GetWorld();
1344 
1345  if (cRoot::Get()->GetPluginManager()->CallHookPlayerBreakingBlock(*m_Player, a_BlockPos, a_BlockFace, DugBlock, DugMeta))
1346  {
1347  // A plugin doesn't agree with the breaking. Bail out. Send the block back to the client, so that it knows:
1348  m_Player->SendBlocksAround(a_BlockPos, 2);
1349  SendPlayerPosition(); // Prevents the player from falling through the block that was temporarily broken client side.
1350  return;
1351  }
1352 
1353  if (DugBlock == E_BLOCK_AIR)
1354  {
1355  return;
1356  }
1357 
1358  // Apply hunger:
1359  m_Player->AddFoodExhaustion(0.025);
1360 
1361  // Damage the tool, but not for 0 hardness blocks:
1363 
1364  cChunkInterface ChunkInterface(World->GetChunkMap());
1365  Vector3i absPos(a_BlockPos);
1367  {
1368  World->DropBlockAsPickups(absPos, m_Player, &m_Player->GetEquippedItem());
1369  }
1370  else
1371  {
1372  World->DigBlock(absPos, m_Player);
1373  }
1374 
1375  World->BroadcastSoundParticleEffect(EffectID::PARTICLE_BLOCK_BREAK, absPos, DugBlock, this);
1376  cRoot::Get()->GetPluginManager()->CallHookPlayerBrokenBlock(*m_Player, a_BlockPos, a_BlockFace, DugBlock, DugMeta);
1377 }
1378 
1379 
1380 
1381 
1382 
1384 {
1385  if (!m_HasStartedDigging) // Hasn't received the DIG_STARTED packet
1386  {
1387  return;
1388  }
1389 
1390  m_HasStartedDigging = false;
1391  if (m_BlockDigAnimStage != -1)
1392  {
1393  // End dig animation
1394  m_BlockDigAnimStage = -1;
1395  // It seems that 10 ends block animation
1397  }
1398 
1400 }
1401 
1402 
1403 
1404 
1405 
1406 void cClientHandle::HandleRightClick(Vector3i a_BlockPos, eBlockFace a_BlockFace, Vector3i a_CursorPos, bool a_UsedMainHand)
1407 {
1408  /* This function handles three actions:
1409  (1) Place a block;
1410  (2) "Use" a block: Interactive with the block, like opening a chest/crafting table/furnace;
1411  (3) Use the held item targeting a block. E.g. farming.
1412 
1413  Sneaking player will not use the block if hand is not empty.
1414  Frozen player can do nothing.
1415  In Game Mode Spectator, player cannot use item or place block, but can interactive with some block depending on cBlockInfo::IsUseableBySpectator(BlockType)
1416 
1417  If the action failed, we need to send an update of the placed block or inventory to the client.
1418 
1419  Actions rejected by plugin will not lead to other attempts.
1420  E.g., when opening a chest with a dirt in hand, if the plugin rejects opening the chest, the dirt will not be placed. */
1421 
1422  if ((a_BlockFace == BLOCK_FACE_NONE) || !cChunkDef::IsValidHeight(a_BlockPos))
1423  {
1424  LOGD("Player \"%s\" sent an invalid click - hacked client?", m_Username.c_str());
1425  return;
1426  }
1427 
1428  // TODO: We are still consuming the items in main hand. Remove this override when the off-hand consumption is handled correctly.
1429  a_UsedMainHand = true;
1430 
1431  cWorld * World = m_Player->GetWorld();
1432  cPluginManager * PlgMgr = cRoot::Get()->GetPluginManager();
1433  const cItem & HeldItem = a_UsedMainHand ? m_Player->GetEquippedItem() : m_Player->GetInventory().GetShieldSlot();
1434 
1435  FLOGD("HandleRightClick: {0}, face {1}, Cursor {2}, Hand: {3}, HeldItem: {4}", a_BlockPos, a_BlockFace, a_CursorPos, a_UsedMainHand, ItemToFullString(HeldItem));
1436 
1437  if (!PlgMgr->CallHookPlayerRightClick(*m_Player, a_BlockPos, a_BlockFace, a_CursorPos) && IsWithinReach(a_BlockPos) && !m_Player->IsFrozen())
1438  {
1440  NIBBLETYPE BlockMeta;
1441 
1442  if (!World->GetBlockTypeMeta(a_BlockPos, BlockType, BlockMeta))
1443  {
1444  return;
1445  }
1446 
1447  const auto & ItemHandler = HeldItem.GetHandler();
1448  const auto & BlockHandler = cBlockHandler::For(BlockType);
1449  const bool BlockUsable = BlockHandler.IsUseable() && (m_Player->IsGameModeSpectator() ? cBlockInfo::IsUseableBySpectator(BlockType) : !(m_Player->IsCrouched() && !HeldItem.IsEmpty()));
1450  const bool ItemPlaceable = ItemHandler.IsPlaceable() && !m_Player->IsGameModeAdventure() && !m_Player->IsGameModeSpectator();
1451  const bool ItemUseable = !m_Player->IsGameModeSpectator();
1452 
1453  if (BlockUsable)
1454  {
1455  cChunkInterface ChunkInterface(World->GetChunkMap());
1456  if (!PlgMgr->CallHookPlayerUsingBlock(*m_Player, a_BlockPos, a_BlockFace, a_CursorPos, BlockType, BlockMeta))
1457  {
1458  // Use a block:
1459  if (BlockHandler.OnUse(ChunkInterface, *World, *m_Player, a_BlockPos, a_BlockFace, a_CursorPos))
1460  {
1461  PlgMgr->CallHookPlayerUsedBlock(*m_Player, a_BlockPos, a_BlockFace, a_CursorPos, BlockType, BlockMeta);
1462  return; // Block use was successful, we're done.
1463  }
1464 
1465  // If block use failed, fall back to placement:
1466  if (ItemPlaceable)
1467  {
1468  // Place a block:
1469  ItemHandler.OnPlayerPlace(*m_Player, HeldItem, a_BlockPos, BlockType, BlockMeta, a_BlockFace, a_CursorPos);
1470  }
1471 
1472  return;
1473  }
1474  }
1475  else if (ItemPlaceable)
1476  {
1477  // TODO: Double check that we don't need this for using item and for packet out of range
1480  {
1481  Kick("Too many blocks were placed / interacted with per unit time - hacked client?");
1482  return;
1483  }
1484 
1485  // Place a block:
1486  ItemHandler.OnPlayerPlace(*m_Player, HeldItem, a_BlockPos, BlockType, BlockMeta, a_BlockFace, a_CursorPos);
1487  return;
1488  }
1489  else if (ItemUseable)
1490  {
1491  if (!PlgMgr->CallHookPlayerUsingItem(*m_Player, a_BlockPos, a_BlockFace, a_CursorPos))
1492  {
1493  // All plugins agree with using the item.
1494  // Use an item in hand with a target block.
1495 
1496  cBlockInServerPluginInterface PluginInterface(*World);
1497  ItemHandler.OnItemUse(World, m_Player, PluginInterface, HeldItem, a_BlockPos, a_BlockFace);
1498  PlgMgr->CallHookPlayerUsedItem(*m_Player, a_BlockPos, a_BlockFace, a_CursorPos);
1499  return;
1500  }
1501  }
1502  else
1503  {
1504  // (x) None of the above.
1505  return;
1506  }
1507  }
1508 
1509  // TODO: delete OnItemUse bool return, delete onCancelRightClick
1510 
1511  // Update the target block including the block above and below for 2 block high things:
1512  m_Player->SendBlocksAround(a_BlockPos, 2);
1513 
1514  // TODO: Send corresponding slot based on hand
1516 }
1517 
1518 
1519 
1520 
1521 
1522 void cClientHandle::HandleChat(const AString & a_Message)
1523 {
1524  if ((a_Message.size()) > MAX_CHAT_MSG_LENGTH)
1525  {
1526  LOGD("Player \"%s\" sent a chat message exceeding the maximum length - hacked client?", m_Username.c_str());
1527  return;
1528  }
1529 
1530  // If a command, perform it:
1531  AString Message(a_Message);
1532  if (cRoot::Get()->GetServer()->Command(*this, Message))
1533  {
1534  return;
1535  }
1536 
1537  // Not a command, broadcast as a message:
1538  cCompositeChat Msg;
1539  AString Color = m_Player->GetColor();
1540  if (Color.length() == 3)
1541  {
1542  Color = AString("@") + Color[2];
1543  }
1544  else
1545  {
1546  Color.clear();
1547  }
1548  Msg.AddTextPart("<");
1549  Msg.ParseText(m_Player->GetPrefix());
1550  Msg.AddTextPart(m_Player->GetName(), Color);
1551  Msg.ParseText(m_Player->GetSuffix());
1552  Msg.AddTextPart("> ");
1553  if (m_Player->HasPermission("cuberite.chat.format"))
1554  {
1555  Msg.ParseText(Message);
1556  }
1557  else
1558  {
1559  Msg.AddTextPart(Message);
1560  }
1561  Msg.UnderlineUrls();
1562  cRoot::Get()->BroadcastChat(Msg);
1563 }
1564 
1565 
1566 
1567 
1568 
1569 void cClientHandle::HandlePlayerLook(float a_Rotation, float a_Pitch, bool a_IsOnGround)
1570 {
1571  m_Player->SetYaw (a_Rotation);
1572  m_Player->SetHeadYaw (a_Rotation);
1573  m_Player->SetPitch (a_Pitch);
1574  m_Player->SetTouchGround(a_IsOnGround);
1575 }
1576 
1577 
1578 
1579 
1580 
1581 void cClientHandle::HandlePlayerMove(Vector3d a_Pos, bool a_IsOnGround)
1582 {
1583  const Vector3d OldPosition = GetPlayer()->GetPosition();
1584  const auto PreviousIsOnGround = GetPlayer()->IsOnGround();
1585 
1586 #ifdef __clang__
1587 #pragma clang diagnostic push
1588 #pragma clang diagnostic ignored "-Wfloat-equal"
1589 #endif
1590 
1591  if (
1592  (OldPosition == a_Pos) &&
1593  (PreviousIsOnGround == a_IsOnGround)
1594  )
1595  {
1596  // Nothing changed, no need to do anything:
1597  return;
1598  }
1599 
1600 #ifdef __clang__
1601 #pragma clang diagnostic pop
1602 #endif
1603 
1604  if (m_Player->IsFrozen() || (m_Player->GetHealth() <= 0))
1605  {
1606  // "Repair" if the client tries to move while frozen or dead:
1608  return;
1609  }
1610 
1611  // If the player has moved too far, "repair" them:
1612  if ((OldPosition - a_Pos).SqrLength() > 100 * 100)
1613  {
1614  LOGD("Too far away (%0.2f), \"repairing\" the client", (OldPosition - a_Pos).Length());
1616  return;
1617  }
1618 
1619  if (cRoot::Get()->GetPluginManager()->CallHookPlayerMoving(*m_Player, OldPosition, a_Pos, PreviousIsOnGround))
1620  {
1622  return;
1623  }
1624 
1625  // TODO: should do some checks to see if player is not moving through terrain
1626  // TODO: Official server refuses position packets too far away from each other, kicking "hacked" clients; we should, too
1627 
1628  m_Player->SetPosition(a_Pos);
1629  m_Player->SetTouchGround(a_IsOnGround);
1630  m_Player->UpdateMovementStats(a_Pos - OldPosition, PreviousIsOnGround);
1631 }
1632 
1633 
1634 
1635 
1636 
1637 void cClientHandle::HandlePlayerMoveLook(Vector3d a_Pos, float a_Rotation, float a_Pitch, bool a_IsOnGround)
1638 {
1639  HandlePlayerMove(a_Pos, a_IsOnGround);
1640  HandlePlayerLook(a_Rotation, a_Pitch, a_IsOnGround);
1641 }
1642 
1643 
1644 
1645 
1646 
1648 {
1651 }
1652 
1653 
1654 
1655 
1656 
1657 void cClientHandle::HandleSpectate(const cUUID & a_PlayerUUID)
1658 {
1659  if (!m_Player->IsGameModeSpectator())
1660  {
1661  LOGD("Player \"%s\" tried to spectate when not in spectator mode - hacked client?", m_Username.c_str());
1662  return;
1663  }
1664 
1665  m_Player->GetWorld()->DoWithPlayerByUUID(a_PlayerUUID, [=](cPlayer & a_ToSpectate)
1666  {
1667  m_Player->TeleportToEntity(a_ToSpectate);
1668  return true;
1669  });
1670 }
1671 
1672 
1673 
1674 
1675 
1676 void cClientHandle::HandleSprint(const bool a_IsSprinting)
1677 {
1678  m_Player->SetSprint(a_IsSprinting);
1679 }
1680 
1681 
1682 
1683 
1684 
1686 {
1687  m_Player->SetElytraFlight(true);
1688 }
1689 
1690 
1691 
1692 
1693 
1694 void cClientHandle::HandleSteerVehicle(float a_Forward, float a_Sideways)
1695 {
1696  m_Player->SteerVehicle(a_Forward, a_Sideways);
1697 }
1698 
1699 
1700 
1701 
1702 
1704 {
1705  m_Player->CloseWindowIfID(static_cast<char>(a_WindowID));
1706 }
1707 
1708 
1709 
1710 
1711 
1712 void cClientHandle::HandleWindowClick(UInt8 a_WindowID, Int16 a_SlotNum, eClickAction a_ClickAction, const cItem & a_HeldItem)
1713 {
1714  LOGD("WindowClick: WinID %d, SlotNum %d, action: %s, Item %s x %d",
1715  a_WindowID, a_SlotNum, ClickActionToString(a_ClickAction),
1716  ItemToString(a_HeldItem).c_str(), a_HeldItem.m_ItemCount
1717  );
1718 
1719  cWindow * Window = m_Player->GetWindow();
1720  if (Window == nullptr)
1721  {
1722  LOGWARNING("Player \"%s\" clicked in a non-existent window. Ignoring", m_Username.c_str());
1723  return;
1724  }
1725  m_Player->AddKnownItem(a_HeldItem);
1726  Window->Clicked(*m_Player, a_WindowID, a_SlotNum, a_ClickAction, a_HeldItem);
1727 }
1728 
1729 
1730 
1731 
1732 
1734  Vector3i a_BlockPos,
1735  const AString & a_Line1, const AString & a_Line2,
1736  const AString & a_Line3, const AString & a_Line4
1737 )
1738 {
1739  if (m_LastPlacedSign.Equals(a_BlockPos))
1740  {
1741  m_LastPlacedSign.Set(0, -1, 0);
1742  m_Player->GetWorld()->SetSignLines(a_BlockPos, a_Line1, a_Line2, a_Line3, a_Line4, m_Player);
1743  }
1744 }
1745 
1746 
1747 
1748 
1749 
1750 void cClientHandle::HandleUseEntity(UInt32 a_TargetEntityID, bool a_IsLeftClick)
1751 {
1752  // TODO: Let plugins interfere via a hook
1753 
1754  // If the player is a spectator, let him spectate
1755  if (m_Player->IsGameModeSpectator() && a_IsLeftClick)
1756  {
1757  m_Player->GetWorld()->DoWithEntityByID(a_TargetEntityID, [=](cEntity & a_Entity)
1758  {
1759  m_Player->SpectateEntity(&a_Entity);
1760  return true;
1761  });
1762  return;
1763  }
1764 
1765  // If it is a right click, call the entity's OnRightClicked() handler:
1766  if (!a_IsLeftClick)
1767  {
1768  cWorld * World = m_Player->GetWorld();
1769  World->DoWithEntityByID(a_TargetEntityID, [=](cEntity & a_Entity)
1770  {
1771  if (
1772  cPluginManager::Get()->CallHookPlayerRightClickingEntity(*m_Player, a_Entity) ||
1773  (
1774  m_Player->IsGameModeSpectator() && // Spectators cannot interact with every entity
1775  (
1776  !a_Entity.IsMinecart() || // They can only interact with minecarts
1777  (
1778  (static_cast<cMinecart &>(a_Entity).GetPayload() != cMinecart::mpChest) && // And only if the type matches a minecart with a chest or
1779  (static_cast<cMinecart &>(a_Entity).GetPayload() != cMinecart::mpHopper) // a minecart with a hopper
1780  )
1781  )
1782  )
1783  )
1784  {
1785  return false;
1786  }
1787  a_Entity.OnRightClicked(*m_Player);
1788  return false;
1789  }
1790  );
1791  return;
1792  }
1793 
1794  // If it is a left click, attack the entity:
1795  m_Player->GetWorld()->DoWithEntityByID(a_TargetEntityID, [=](cEntity & a_Entity)
1796  {
1797  if (!a_Entity.GetWorld()->IsPVPEnabled())
1798  {
1799  // PVP is disabled, disallow players hurting other players:
1800  if (a_Entity.IsPlayer())
1801  {
1802  // Player is hurting another player which is not allowed when PVP is disabled so ignore it
1803  return true;
1804  }
1805  }
1806  a_Entity.TakeDamage(*m_Player);
1808  if (a_Entity.IsPawn())
1809  {
1810  m_Player->NotifyNearbyWolves(static_cast<cPawn*>(&a_Entity), true);
1811  }
1812  return true;
1813  }
1814  );
1815 }
1816 
1817 
1818 
1819 
1820 
1821 void cClientHandle::HandleUseItem(bool a_UsedMainHand)
1822 {
1823  // Use the held item without targeting a block: eating, drinking, charging a bow, using buckets
1824  // In version 1.8.x, this function shares the same packet id with HandleRightClick.
1825  // In version >= 1.9, there is a new packet id for "Use Item".
1826 
1827  // TODO: We are still consuming the items in main hand. Remove this override when the off-hand consumption is handled correctly.
1828  a_UsedMainHand = true;
1829  const cItem & HeldItem = a_UsedMainHand ? m_Player->GetEquippedItem() : m_Player->GetInventory().GetShieldSlot();
1830  auto & ItemHandler = HeldItem.GetHandler();
1831  cWorld * World = m_Player->GetWorld();
1832  cPluginManager * PlgMgr = cRoot::Get()->GetPluginManager();
1833 
1834  LOGD("HandleUseItem: Hand: %d; HeldItem: %s", a_UsedMainHand, ItemToFullString(HeldItem).c_str());
1835 
1836  constexpr Vector3i DefaultBlockPos(-1, 255, -1);
1837  constexpr Vector3i DefaultCursorPos(0, 0, 0);
1838 
1839  if (PlgMgr->CallHookPlayerRightClick(*m_Player, DefaultBlockPos, BLOCK_FACE_NONE, DefaultCursorPos))
1840  {
1841  return; // Plugin denied click action
1842  }
1843 
1844  // Use item in main / off hand
1845  // TODO: do we need to sync the current inventory with client if it fails?
1847  {
1848  return;
1849  }
1850 
1851  if (ItemHandler.IsFood() || ItemHandler.IsDrinkable(HeldItem.m_ItemDamage))
1852  {
1853  if (
1854  ItemHandler.IsFood() &&
1855  (m_Player->IsSatiated() || m_Player->IsGameModeCreative()) && // Only non-creative or hungry players can eat
1856  (HeldItem.m_ItemType != E_ITEM_GOLDEN_APPLE) && // Golden apple is a special case, it is used instead of eaten
1857  (HeldItem.m_ItemType != E_ITEM_CHORUS_FRUIT) // Chorus fruit is a special case, it is used instead of eaten
1858  )
1859  {
1860  // The player is satiated or in creative, and trying to eat
1861  return;
1862  }
1863  if (!PlgMgr->CallHookPlayerEating(*m_Player))
1864  {
1865  m_Player->StartEating();
1866  }
1867  }
1868  else
1869  {
1870  // Use an item in hand without a target block
1871  if (!PlgMgr->CallHookPlayerUsingItem(*m_Player, DefaultBlockPos, BLOCK_FACE_NONE, DefaultCursorPos))
1872  {
1873  // All plugins agree with using the item
1874  cBlockInServerPluginInterface PluginInterface(*World);
1875  ItemHandler.OnItemUse(World, m_Player, PluginInterface, HeldItem, DefaultBlockPos, BLOCK_FACE_NONE);
1876  PlgMgr->CallHookPlayerUsedItem(*m_Player, DefaultBlockPos, BLOCK_FACE_NONE, DefaultCursorPos);
1877  }
1878  }
1879 }
1880 
1881 
1882 
1883 
1884 
1886 {
1887  // Kick player if client declined the resource pack
1888  if ((a_Status == 1) && cRoot::Get()->GetServer()->ShouldRequireResourcePack())
1889  {
1890  Kick("You must accept the resource pack");
1891  }
1892 }
1893 
1894 
1895 
1896 
1897 
1899 {
1900  if (m_Player->GetHealth() > 0)
1901  {
1902  LOGD("Player \"%s\" tried to respawn while alive - hacked client?", m_Username.c_str());
1903  return;
1904  }
1905 
1906  m_Player->Respawn();
1908 }
1909 
1910 
1911 
1912 
1913 
1915 {
1916  if (a_KeepAliveID == m_PingID)
1917  {
1918  m_Ping = std::chrono::steady_clock::now() - m_PingStartTime;
1919  }
1920 }
1921 
1922 
1923 
1924 
1925 
1926 bool cClientHandle::CheckMultiLogin(const AString & a_Username)
1927 {
1928  // If the multilogin is allowed, skip this check entirely:
1929  if ((cRoot::Get()->GetServer()->DoesAllowMultiLogin()))
1930  {
1931  return true;
1932  }
1933 
1934  // Check if the player is waiting to be transferred to the World.
1935  if (cRoot::Get()->GetServer()->IsPlayerInQueue(a_Username))
1936  {
1937  Kick("A player of the username is already logged in");
1938  return false;
1939  }
1940 
1941  // Check if the player is in any World.
1942  if (cRoot::Get()->DoWithPlayer(a_Username, [](cPlayer &) { return true; }))
1943  {
1944  Kick("A player of the username is already logged in");
1945  return false;
1946  }
1947  return true;
1948 }
1949 
1950 
1951 
1952 
1953 
1954 bool cClientHandle::HandleHandshake(const AString & a_Username)
1955 {
1956  if (a_Username.length() > 16)
1957  {
1958  Kick("Your username is too long (>16 characters)");
1959  return false;
1960  }
1961 
1962  if (cRoot::Get()->GetPluginManager()->CallHookHandshake(*this, a_Username))
1963  {
1964  Kick("Entry denied by plugin");
1965  return false;
1966  }
1967 
1968  return CheckMultiLogin(a_Username);
1969 }
1970 
1971 
1972 
1973 
1974 
1976 {
1977  cChunkInterface Interface(m_Player->GetWorld()->GetChunkMap());
1979 }
1980 
1981 
1982 
1983 
1984 
1986 {
1987  m_Player->Detach();
1988 }
1989 
1990 
1991 
1992 
1993 
1995 {
1996  AStringVector Results;
1997  // Get player name completions.
1998  if (cRoot::Get()->GetServer()->ShouldAllowMultiWorldTabCompletion())
1999  {
2000  Results = cRoot::Get()->GetPlayerTabCompletionMultiWorld(a_Text);
2001  }
2002  else
2003  {
2004  m_Player->GetWorld()->TabCompleteUserName(a_Text, Results);
2005  }
2006 
2007  // Get command completions.
2008  cRoot::Get()->GetPluginManager()->TabCompleteCommand(a_Text, Results, m_Player);
2009  if (Results.empty())
2010  {
2011  return;
2012  }
2013 
2014  // Sort and send results.
2015  std::sort(Results.begin(), Results.end());
2016  SendTabCompletionResults(Results);
2017 }
2018 
2019 
2020 
2021 
2022 
2024 {
2025  if (m_HasSentDC)
2026  {
2027  // This could crash the client, because they've already unloaded the world etc., and suddenly a wild packet appears (#31)
2028  return;
2029  }
2030 
2031  cCSLock Lock(m_CSOutgoingData);
2032  m_OutgoingData += a_Data;
2033 }
2034 
2035 
2036 
2037 
2038 
2040 {
2041  // Remove all associated chunks:
2042  {
2043  cCSLock Lock(m_CSChunkLists);
2044  m_LoadedChunks.clear();
2045  m_ChunksToSend.clear();
2046  m_SentChunks.clear();
2047  }
2048 
2049  // Flush outgoing data:
2051 
2052  // No need to send Unload Chunk packets, the client unloads automatically.
2053 
2054  // Here, we set last streamed values to bogus ones so everything is resent:
2055  m_LastStreamedChunkX = std::numeric_limits<decltype(m_LastStreamedChunkX)>::max();
2056  m_LastStreamedChunkZ = std::numeric_limits<decltype(m_LastStreamedChunkZ)>::max();
2057 
2058  // Restart player unloaded chunk checking and freezing:
2059  m_CachedSentChunk = cChunkCoords(std::numeric_limits<decltype(m_CachedSentChunk.m_ChunkX)>::max(), std::numeric_limits<decltype(m_CachedSentChunk.m_ChunkZ)>::max());
2060 }
2061 
2062 
2063 
2064 
2065 
2067 {
2068  return m_HasSentPlayerChunk;
2069 }
2070 
2071 
2072 
2073 
2074 
2076 {
2077  ASSERT(m_Player != nullptr);
2078  ASSERT(m_Player->GetWorld() != nullptr);
2079 
2080  if (!cRoot::Get()->GetServer()->ShouldLimitPlayerBlockChanges())
2081  {
2082  return true;
2083  }
2084 
2086 }
2087 
2088 
2089 
2090 
2091 
2092 bool cClientHandle::IsWithinReach(const Vector3i a_Position) const
2093 {
2094  // Distance from the block's center to the player's eye height.
2095  const double Distance = (Vector3d(0.5, 0.5, 0.5) + a_Position - m_Player->GetEyePosition()).SqrLength();
2096 
2097  // _X 2014-11-25: I've maxed at 5.26 with a Survival client and 5.78 with a Creative client in my tests.
2098  return Distance <= (m_Player->IsGameModeCreative() ? 33.4084 : 27.6676);
2099 }
2100 
2101 
2102 
2103 
2104 
2105 void cClientHandle::Tick(std::chrono::milliseconds a_Dt)
2106 {
2107  using namespace std::chrono_literals;
2108 
2109  if (IsDestroyed())
2110  {
2111  return;
2112  }
2113 
2115  if (m_TicksSinceLastPacket > 600) // 30 seconds time-out
2116  {
2117  SendDisconnect("Nooooo!! You timed out! D: Come back!");
2118  return;
2119  }
2120 
2121  // Freeze the player if they are standing in a chunk not yet sent to the client
2122  m_HasSentPlayerChunk = false;
2123  if (m_Player->GetParentChunk() != nullptr)
2124  {
2125  // If the chunk is invalid, it has definitely not been sent to the client yet
2126  if (m_Player->GetParentChunk()->IsValid())
2127  {
2128  // Before iterating m_SentChunks, see if the player's coords equal m_CachedSentChunk
2129  // If so, the chunk has been sent to the client. This is an optimization that saves an iteration of m_SentChunks.
2131  {
2132  m_HasSentPlayerChunk = true;
2133  }
2134  else
2135  {
2136  // This block is entered only when the player moves to a new chunk, invalidating the cached coords.
2137  // Otherwise the cached coords are used.
2138  cCSLock Lock(m_CSChunkLists);
2139  auto itr = std::find(m_SentChunks.begin(), m_SentChunks.end(), cChunkCoords(m_Player->GetChunkX(), m_Player->GetChunkZ()));
2140  if (itr != m_SentChunks.end())
2141  {
2142  m_CachedSentChunk = *itr;
2143  m_HasSentPlayerChunk = true;
2144  }
2145  }
2146  }
2147  }
2148 
2149  // If the chunk the player's in was just sent, spawn the player:
2150  {
2151  cCSLock lock(m_CSState);
2153  {
2154  m_Protocol->SendPlayerMoveLook();
2155  m_State = csPlaying;
2156 
2157  // Send resource pack (after a MoveLook, because sending it before the initial MoveLook cancels the download screen):
2158  if (const auto & ResourcePackUrl = cRoot::Get()->GetServer()->GetResourcePackUrl(); !ResourcePackUrl.empty())
2159  {
2160  SendResourcePack(ResourcePackUrl);
2161  }
2162  }
2163  } // lock(m_CSState)
2164 
2165  // Send a ping packet:
2166  if (m_State == csPlaying)
2167  {
2168  const auto Now = std::chrono::steady_clock::now();
2169  if ((m_PingStartTime + std::chrono::seconds(2)) <= Now) // 2 second interval between pings
2170  {
2171  m_PingID++;
2172  m_PingStartTime = Now;
2173  m_Protocol->SendKeepAlive(m_PingID);
2174  }
2175  }
2176 
2177  // Send a couple of chunks to the player:
2178  StreamNextChunks();
2179 
2180  // Unload all chunks that are out of the view distance (every 5 seconds):
2181  if ((m_TimeSinceLastUnloadCheck += a_Dt) > 5s)
2182  {
2185  }
2186 
2187  // anticheat fastbreak
2188  if (m_HasStartedDigging)
2189  {
2192  }
2193 
2194  // Handle block break animation:
2195  if (m_BlockDigAnimStage > -1)
2196  {
2197  int lastAnimVal = m_BlockDigAnimStage;
2198  m_BlockDigAnimStage += static_cast<int>(m_BlockDigAnimSpeed * a_Dt.count());
2199  if (m_BlockDigAnimStage > 9000)
2200  {
2201  m_BlockDigAnimStage = 9000;
2202  }
2203  if (m_BlockDigAnimStage / 1000 != lastAnimVal / 1000)
2204  {
2205  m_Player->GetWorld()->BroadcastBlockBreakAnimation(static_cast<UInt32>(m_UniqueID), m_BlockDigAnimPos, static_cast<char>(m_BlockDigAnimStage / 1000), this);
2206  }
2207  }
2208 
2209  // Reset explosion & block change counters:
2212 }
2213 
2214 
2215 
2216 
2217 
2219 {
2222 
2224  if (m_TicksSinceLastPacket > 600) // 30 seconds
2225  {
2226  SendDisconnect("Nooooo!! You timed out! D: Come back!");
2227  }
2228 }
2229 
2230 
2231 
2232 
2233 
2234 void cClientHandle::SendAttachEntity(const cEntity & a_Entity, const cEntity & a_Vehicle)
2235 {
2236  m_Protocol->SendAttachEntity(a_Entity, a_Vehicle);
2237 }
2238 
2239 
2240 
2241 
2242 
2243 void cClientHandle::SendLeashEntity(const cEntity & a_Entity, const cEntity & a_EntityLeashedTo)
2244 {
2245  m_Protocol->SendLeashEntity(a_Entity, a_EntityLeashedTo);
2246 }
2247 
2248 
2249 
2250 
2251 
2253 {
2254  m_Protocol->SendUnleashEntity(a_Entity);
2255 }
2256 
2257 
2258 
2259 
2260 
2261 void cClientHandle::SendBlockAction(Vector3i a_BlockPos, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType)
2262 {
2263  m_Protocol->SendBlockAction(a_BlockPos, a_Byte1, a_Byte2, a_BlockType);
2264 }
2265 
2266 
2267 
2268 
2269 
2270 void cClientHandle::SendBlockBreakAnim(UInt32 a_EntityID, Vector3i a_BlockPos, char a_Stage)
2271 {
2272  m_Protocol->SendBlockBreakAnim(a_EntityID, a_BlockPos, a_Stage);
2273 }
2274 
2275 
2276 
2277 
2278 
2279 void cClientHandle::SendBlockChange(Vector3i a_BlockPos, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
2280 {
2281  auto ChunkCoords = cChunkDef::BlockToChunk(a_BlockPos);
2282 
2283  // Do not send block changes in chunks that weren't sent to the client yet:
2284  cCSLock Lock(m_CSChunkLists);
2285  if (std::find(m_SentChunks.begin(), m_SentChunks.end(), ChunkCoords) != m_SentChunks.end())
2286  {
2287  Lock.Unlock();
2288  m_Protocol->SendBlockChange(a_BlockPos, a_BlockType, a_BlockMeta);
2289  }
2290 }
2291 
2292 
2293 
2294 
2295 
2296 void cClientHandle::SendBlockChanges(int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes)
2297 {
2298  ASSERT(!a_Changes.empty()); // We don't want to be sending empty change packets!
2299 
2300  // Do not send block changes in chunks that weren't sent to the client yet:
2301  {
2302  cCSLock Lock(m_CSChunkLists);
2303  if (std::find(m_SentChunks.begin(), m_SentChunks.end(), cChunkCoords(a_ChunkX, a_ChunkZ)) == m_SentChunks.end())
2304  {
2305  return;
2306  }
2307  }
2308 
2309  // Use a dedicated packet for single changes:
2310  if (a_Changes.size() == 1)
2311  {
2312  const auto & Change = a_Changes[0];
2313  m_Protocol->SendBlockChange(Change.GetAbsolutePos(), Change.m_BlockType, Change.m_BlockMeta);
2314  return;
2315  }
2316 
2317  m_Protocol->SendBlockChanges(a_ChunkX, a_ChunkZ, a_Changes);
2318 }
2319 
2320 
2321 
2322 
2323 
2324 void cClientHandle::SendBossBarAdd(UInt32 a_UniqueID, const cCompositeChat & a_Title, float a_FractionFilled, BossBarColor a_Color, BossBarDivisionType a_DivisionType, bool a_DarkenSky, bool a_PlayEndMusic, bool a_CreateFog)
2325 {
2326  m_Protocol->SendBossBarAdd(a_UniqueID, a_Title, a_FractionFilled, a_Color, a_DivisionType, a_DarkenSky, a_PlayEndMusic, a_CreateFog);
2327 }
2328 
2329 
2330 
2331 
2332 
2333 void cClientHandle::SendBossBarUpdateFlags(UInt32 a_UniqueID, bool a_DarkenSky, bool a_PlayEndMusic, bool a_CreateFog)
2334 {
2335  m_Protocol->SendBossBarUpdateFlags(a_UniqueID, a_DarkenSky, a_PlayEndMusic, a_CreateFog);
2336 }
2337 
2338 
2339 
2340 
2341 
2343 {
2344  m_Protocol->SendBossBarUpdateStyle(a_UniqueID, a_Color, a_DivisionType);
2345 }
2346 
2347 
2348 
2349 
2350 
2352 {
2353  m_Protocol->SendBossBarUpdateTitle(a_UniqueID, a_Title);
2354 }
2355 
2356 
2357 
2358 
2359 
2361 {
2362  m_Protocol->SendBossBarRemove(a_UniqueID);
2363 }
2364 
2365 
2366 
2367 
2368 
2369 void cClientHandle::SendBossBarUpdateHealth(UInt32 a_UniqueID, float a_FractionFilled)
2370 {
2371  m_Protocol->SendBossBarUpdateHealth(a_UniqueID, a_FractionFilled);
2372 }
2373 
2374 
2375 
2376 
2377 
2379 {
2380  m_Protocol->SendCameraSetTo(a_Entity);
2381 }
2382 
2383 
2384 
2385 
2386 
2387 void cClientHandle::SendChat(const AString & a_Message, eMessageType a_ChatPrefix, const AString & a_AdditionalData)
2388 {
2389  cWorld * World = GetPlayer()->GetWorld();
2390  if (World == nullptr)
2391  {
2392  World = cRoot::Get()->GetWorld(GetPlayer()->GetLoadedWorldName());
2393  if (World == nullptr)
2394  {
2396  }
2397  }
2398 
2399  bool ShouldUsePrefixes = World->ShouldUseChatPrefixes();
2400  AString Message = FormatMessageType(ShouldUsePrefixes, a_ChatPrefix, a_AdditionalData);
2401  m_Protocol->SendChat(Message.append(a_Message), ctChatBox, ShouldUsePrefixes);
2402 }
2403 
2404 
2405 
2406 
2407 
2409 {
2410  cWorld * World = GetPlayer()->GetWorld();
2411  if (World == nullptr)
2412  {
2413  World = cRoot::Get()->GetWorld(GetPlayer()->GetLoadedWorldName());
2414  if (World == nullptr)
2415  {
2417  }
2418  }
2419 
2420  bool ShouldUsePrefixes = World->ShouldUseChatPrefixes();
2421  m_Protocol->SendChat(a_Message, ctChatBox, ShouldUsePrefixes);
2422 }
2423 
2424 
2425 
2426 
2427 
2428 void cClientHandle::SendChatRaw(const AString & a_MessageRaw, eChatType a_Type)
2429 {
2430  m_Protocol->SendChatRaw(a_MessageRaw, a_Type);
2431 }
2432 
2433 
2434 
2435 
2436 
2437 void cClientHandle::SendChatAboveActionBar(const AString & a_Message, eMessageType a_ChatPrefix, const AString & a_AdditionalData)
2438 {
2439  cWorld * World = GetPlayer()->GetWorld();
2440  if (World == nullptr)
2441  {
2442  World = cRoot::Get()->GetWorld(GetPlayer()->GetLoadedWorldName());
2443  if (World == nullptr)
2444  {
2446  }
2447  }
2448 
2449  AString Message = FormatMessageType(World->ShouldUseChatPrefixes(), a_ChatPrefix, a_AdditionalData);
2450  m_Protocol->SendChat(Message.append(a_Message), ctAboveActionBar);
2451 }
2452 
2453 
2454 
2455 
2456 
2458 {
2459  m_Protocol->SendChat(a_Message, ctAboveActionBar, GetPlayer()->GetWorld()->ShouldUseChatPrefixes());
2460 }
2461 
2462 
2463 
2464 
2465 
2466 void cClientHandle::SendChatSystem(const AString & a_Message, eMessageType a_ChatPrefix, const AString & a_AdditionalData)
2467 {
2468  cWorld * World = GetPlayer()->GetWorld();
2469  if (World == nullptr)
2470  {
2471  World = cRoot::Get()->GetWorld(GetPlayer()->GetLoadedWorldName());
2472  if (World == nullptr)
2473  {
2475  }
2476  }
2477 
2478  auto ShouldUsePrefixes = World->ShouldUseChatPrefixes();
2479  AString Message = FormatMessageType(ShouldUsePrefixes, a_ChatPrefix, a_AdditionalData);
2480  m_Protocol->SendChat(Message.append(a_Message), ctSystem, ShouldUsePrefixes);
2481 }
2482 
2483 
2484 
2485 
2486 
2488 {
2489  m_Protocol->SendChat(a_Message, ctSystem, GetPlayer()->GetWorld()->ShouldUseChatPrefixes());
2490 }
2491 
2492 
2493 
2494 
2495 
2496 void cClientHandle::SendChunkData(int a_ChunkX, int a_ChunkZ, const ContiguousByteBufferView a_ChunkData)
2497 {
2498  ASSERT(m_Player != nullptr);
2499 
2500  // Check chunks being sent, erase them from m_ChunksToSend:
2501  bool Found = false;
2502  {
2503  cCSLock Lock(m_CSChunkLists);
2504  auto itr = m_ChunksToSend.find(cChunkCoords{a_ChunkX, a_ChunkZ});
2505  if (itr != m_ChunksToSend.end())
2506  {
2507  m_ChunksToSend.erase(itr);
2508  Found = true;
2509  }
2510  }
2511  if (!Found)
2512  {
2513  // This just sometimes happens. If you have a reliably replicatable situation for this, go ahead and fix it
2514  // It's not a big issue anyway, just means that some chunks may be compressed several times
2515  // LOG("Refusing to send chunk [%d, %d] to client \"%s\" at [%d, %d].", a_ChunkX, a_ChunkZ, m_Username.c_str(), m_Player->GetChunkX(), m_Player->GetChunkZ());
2516  // 2020 08 21: seems to happen going through nether portals on 1.8.9
2517  return;
2518  }
2519 
2521  {
2522  // TODO (#2588): investigate if and why this occurs
2523  return;
2524  }
2525 
2526  m_Protocol->SendChunkData(a_ChunkData);
2527 
2528  // Add the chunk to the list of chunks sent to the player:
2529  {
2530  cCSLock Lock(m_CSChunkLists);
2531  m_SentChunks.emplace_back(a_ChunkX, a_ChunkZ);
2532  }
2533 }
2534 
2535 
2536 
2537 
2538 
2539 void cClientHandle::SendCollectEntity(const cEntity & a_Collected, const cEntity & a_Collector, unsigned a_Count)
2540 {
2541  m_Protocol->SendCollectEntity(a_Collected, a_Collector, a_Count);
2542 }
2543 
2544 
2545 
2546 
2547 
2549 {
2550  m_Protocol->SendDestroyEntity(a_Entity);
2551 }
2552 
2553 
2554 
2555 
2556 
2557 void cClientHandle::SendDetachEntity(const cEntity & a_Entity, const cEntity & a_PreviousVehicle)
2558 {
2559  m_Protocol->SendDetachEntity(a_Entity, a_PreviousVehicle);
2560 }
2561 
2562 
2563 
2564 
2565 
2567 {
2568  LOGD("Sending a DC: \"%s\"", StripColorCodes(a_Reason).c_str());
2569 
2570  m_Protocol.SendDisconnect(*this, a_Reason);
2571  m_HasSentDC = true;
2572  Destroy();
2573 }
2574 
2575 
2576 
2577 
2578 
2580 {
2581  m_LastPlacedSign = a_BlockPos;
2582  m_Protocol->SendEditSign(a_BlockPos);
2583 }
2584 
2585 
2586 
2587 
2588 
2589 void cClientHandle::SendEntityEffect(const cEntity & a_Entity, int a_EffectID, int a_Amplifier, int a_Duration)
2590 {
2591  m_Protocol->SendEntityEffect(a_Entity, a_EffectID, a_Amplifier, a_Duration);
2592 }
2593 
2594 
2595 
2596 
2597 
2598 void cClientHandle::SendEntityEquipment(const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item)
2599 {
2600  m_Protocol->SendEntityEquipment(a_Entity, a_SlotNum, a_Item);
2601 }
2602 
2603 
2604 
2605 
2606 
2608 {
2609  ASSERT(a_Entity.GetUniqueID() != m_Player->GetUniqueID()); // Must not send for self
2610 
2611  m_Protocol->SendEntityHeadLook(a_Entity);
2612 }
2613 
2614 
2615 
2616 
2617 
2619 {
2620  ASSERT(a_Entity.GetUniqueID() != m_Player->GetUniqueID()); // Must not send for self
2621 
2622  m_Protocol->SendEntityLook(a_Entity);
2623 }
2624 
2625 
2626 
2627 
2628 
2630 {
2631  m_Protocol->SendEntityMetadata(a_Entity);
2632 }
2633 
2634 
2635 
2636 
2637 
2639 {
2640  m_Protocol->SendEntityPosition(a_Entity);
2641 }
2642 
2643 
2644 
2645 
2646 
2648 {
2649  m_Protocol->SendEntityProperties(a_Entity);
2650 }
2651 
2652 
2653 
2654 
2655 
2657 {
2658  m_Protocol->SendEntityVelocity(a_Entity);
2659 }
2660 
2661 
2662 
2663 
2664 
2665 void cClientHandle::SendExplosion(const Vector3f a_Position, const float a_Power)
2666 {
2668  {
2669  LOGD("Dropped an explosion!");
2670  return;
2671  }
2672 
2673  // Update the statistics:
2675 
2676  auto & Random = GetRandomProvider();
2677  const auto SoundPitchMultiplier = 1.0f + (Random.RandReal() - Random.RandReal()) * 0.2f;
2678 
2679  // Sound:
2680  SendSoundEffect("entity.generic.explode", a_Position, 4.0f, SoundPitchMultiplier * 0.7f);
2681 
2682  const auto ParticleFormula = a_Power * 0.33f;
2683  auto Spread = ParticleFormula * 0.5f;
2684  auto ParticleCount = std::min(static_cast<int>(ParticleFormula * 125), 600);
2685 
2686  // Dark smoke particles:
2687  SendParticleEffect("largesmoke", a_Position, {0.f, 0.f, 0.f}, Spread, static_cast<int>(ParticleCount));
2688 
2689  Spread = ParticleFormula * 0.35f;
2690  ParticleCount = std::min(static_cast<int>(ParticleFormula * 550), 1800);
2691 
2692  // Light smoke particles:
2693  SendParticleEffect("explode", a_Position, {0.f, 0.f, 0.f}, Spread, static_cast<int>(ParticleCount));
2694 
2695  // Shockwave effect:
2696  m_Protocol->SendExplosion(a_Position, a_Power);
2697 }
2698 
2699 
2700 
2701 
2702 
2704 {
2705  m_Protocol->SendGameMode(a_GameMode);
2706 }
2707 
2708 
2709 
2710 
2711 
2713 {
2714  m_Protocol->SendHealth();
2715 }
2716 
2717 
2718 
2719 
2720 
2722 {
2723  m_Protocol->SendHeldItemChange(a_ItemIndex);
2724 }
2725 
2726 
2727 
2728 
2729 
2731 {
2732  m_Protocol->SendHideTitle();
2733 }
2734 
2735 
2736 
2737 
2738 
2739 void cClientHandle::SendInventorySlot(char a_WindowID, short a_SlotNum, const cItem & a_Item)
2740 {
2741  m_Protocol->SendInventorySlot(a_WindowID, a_SlotNum, a_Item);
2742 }
2743 
2744 
2745 
2746 
2747 
2748 void cClientHandle::SendMapData(const cMap & a_Map, int a_DataStartX, int a_DataStartY)
2749 {
2750  m_Protocol->SendMapData(a_Map, a_DataStartX, a_DataStartY);
2751 }
2752 
2753 
2754 
2755 
2756 
2757 void cClientHandle::SendParticleEffect(const AString & a_ParticleName, Vector3f a_Source, Vector3f a_Offset, float a_ParticleData, int a_ParticleAmount)
2758 {
2759  m_Protocol->SendParticleEffect(a_ParticleName, a_Source, a_Offset, a_ParticleData, a_ParticleAmount);
2760 }
2761 
2762 
2763 
2764 
2765 
2766 void cClientHandle::SendParticleEffect(const AString & a_ParticleName, const Vector3f a_Src, const Vector3f a_Offset, float a_ParticleData, int a_ParticleAmount, std::array<int, 2> a_Data)
2767 {
2768  m_Protocol->SendParticleEffect(a_ParticleName, a_Src, a_Offset, a_ParticleData, a_ParticleAmount, a_Data);
2769 }
2770 
2771 
2772 
2773 
2774 
2776 {
2777  m_Protocol->SendPaintingSpawn(a_Painting);
2778 }
2779 
2780 
2781 
2782 
2783 
2785 {
2786  m_Protocol->SendEntityAnimation(a_Entity, a_Animation);
2787 }
2788 
2789 
2790 
2791 
2792 
2794 {
2795  m_Protocol->SendPlayerAbilities();
2796 }
2797 
2798 
2799 
2800 
2801 
2803 {
2804  m_Protocol->SendPlayerListAddPlayer(a_Player);
2805 }
2806 
2807 
2808 
2809 
2810 
2812 {
2813  m_Protocol->SendPlayerListHeaderFooter(a_Header, a_Footer);
2814 }
2815 
2816 
2817 
2818 
2819 
2821 {
2822  m_Protocol->SendPlayerListRemovePlayer(a_Player);
2823 }
2824 
2825 
2826 
2827 
2828 
2829 void cClientHandle::SendPlayerListUpdateDisplayName(const cPlayer & a_Player, const AString & a_CustomName)
2830 {
2831  m_Protocol->SendPlayerListUpdateDisplayName(a_Player, a_CustomName);
2832 }
2833 
2834 
2835 
2836 
2837 
2839 {
2840  m_Protocol->SendPlayerListUpdateGameMode(a_Player);
2841 }
2842 
2843 
2844 
2845 
2846 
2848 {
2849  m_Protocol->SendPlayerListUpdatePing();
2850 }
2851 
2852 
2853 
2854 
2855 
2856 void cClientHandle::SendPlayerMoveLook (const Vector3d a_Pos, const float a_Yaw, const float a_Pitch, const bool a_IsRelative)
2857 {
2858  m_Protocol->SendPlayerMoveLook(a_Pos, a_Yaw, a_Pitch, a_IsRelative);
2859 }
2860 
2861 
2862 
2863 
2864 
2866 {
2867  /*
2868  FLOGD("Sending PlayerMoveLook: {0:0.2f}, stance {1:0.2f}, OnGround: {2}",
2869  m_Player->GetPosition(), m_Player->GetStance(), m_Player->IsOnGround()
2870  );
2871  */
2872  m_Protocol->SendPlayerMoveLook();
2873 }
2874 
2875 
2876 
2877 
2878 
2880 {
2881  m_Protocol->SendPlayerPermissionLevel();
2882 }
2883 
2884 
2885 
2886 
2887 
2889 {
2890  m_Protocol->SendPlayerPosition();
2891 }
2892 
2893 
2894 
2895 
2896 
2898 {
2899  if (a_Player.GetUniqueID() == m_Player->GetUniqueID())
2900  {
2901  // Do NOT send this packet to myself
2902  return;
2903  }
2904 
2905  LOGD("Spawning player \"%s\" on client \"%s\" @ %s",
2906  a_Player.GetName().c_str(), GetPlayer()->GetName().c_str(), GetIPString().c_str()
2907  );
2908 
2909  m_Protocol->SendPlayerSpawn(a_Player);
2910 }
2911 
2912 
2913 
2914 
2915 
2916 void cClientHandle::SendPluginMessage(const AString & a_Channel, const std::string_view a_Message)
2917 {
2918  m_Protocol->SendPluginMessage(a_Channel, { reinterpret_cast<const std::byte *>(a_Message.data()), a_Message.size() });
2919 }
2920 
2921 
2922 
2923 
2924 
2926 {
2927  m_Protocol->SendPluginMessage(a_Channel, a_Message);
2928 }
2929 
2930 
2931 
2932 
2933 
2934 void cClientHandle::SendRemoveEntityEffect(const cEntity & a_Entity, int a_EffectID)
2935 {
2936  m_Protocol->SendRemoveEntityEffect(a_Entity, a_EffectID);
2937 }
2938 
2939 
2940 
2941 
2942 
2944 {
2945  m_Protocol->SendResetTitle();
2946 }
2947 
2948 
2949 
2950 
2951 
2952 void cClientHandle::SendRespawn(const eDimension a_Dimension, const bool a_IsRespawningFromDeath)
2953 {
2954  if (!a_IsRespawningFromDeath && (a_Dimension == m_Player->GetWorld()->GetDimension()))
2955  {
2956  // The client goes crazy if we send a respawn packet with the dimension of the current world
2957  // So we send a temporary one first.
2958  // This is not needed when the player dies, hence the a_IsRespawningFromDeath flag.
2959  // a_IsRespawningFromDeath is true only at cPlayer::Respawn, which is called after the player dies.
2960 
2961  // First send a temporary dimension to placate the client:
2962  m_Protocol->SendRespawn((a_Dimension == dimOverworld) ? dimNether : dimOverworld);
2963  }
2964 
2965  m_Protocol->SendRespawn(a_Dimension);
2966 }
2967 
2968 
2969 
2970 
2971 
2973 {
2974  m_Protocol->SendExperience();
2975 }
2976 
2977 
2978 
2979 
2980 
2982 {
2983  m_Protocol->SendExperienceOrb(a_ExpOrb);
2984 }
2985 
2986 
2987 
2988 
2989 
2990 void cClientHandle::SendResourcePack(const AString & a_ResourcePackUrl)
2991 {
2992  m_Protocol->SendResourcePack(a_ResourcePackUrl);
2993 }
2994 
2995 
2996 
2997 
2998 
2999 void cClientHandle::SendScoreboardObjective(const AString & a_Name, const AString & a_DisplayName, Byte a_Mode)
3000 {
3001  m_Protocol->SendScoreboardObjective(a_Name, a_DisplayName, a_Mode);
3002 }
3003 
3004 
3005 
3006 
3007 
3008 void cClientHandle::SendScoreUpdate(const AString & a_Objective, const AString & a_Player, cObjective::Score a_Score, Byte a_Mode)
3009 {
3010  m_Protocol->SendScoreUpdate(a_Objective, a_Player, a_Score, a_Mode);
3011 }
3012 
3013 
3014 
3015 
3016 
3018 {
3019  m_Protocol->SendDisplayObjective(a_Objective, a_Display);
3020 }
3021 
3022 
3023 
3024 
3025 
3027 {
3028  m_Protocol->SendSetSubTitle(a_SubTitle);
3029 }
3030 
3031 
3032 
3033 
3034 
3036 {
3037  m_Protocol->SendSetRawSubTitle(a_SubTitle);
3038 }
3039 
3040 
3041 
3042 
3043 
3045 {
3046  m_Protocol->SendSetTitle(a_Title);
3047 }
3048 
3049 
3050 
3051 
3052 
3054 {
3055  m_Protocol->SendSetRawTitle(a_Title);
3056 }
3057 
3058 
3059 
3060 
3061 
3062 void cClientHandle::SendSoundEffect(const AString & a_SoundName, double a_X, double a_Y, double a_Z, float a_Volume, float a_Pitch)
3063 {
3064  LOG("SendSoundEffect with double args is deprecated, use version with vector position parameter.");
3065  SendSoundEffect(a_SoundName, {a_X, a_Y, a_Z}, a_Volume, a_Pitch);
3066 }
3067 
3068 
3069 
3070 
3071 
3072 void cClientHandle::SendSoundEffect(const AString & a_SoundName, Vector3d a_Position, float a_Volume, float a_Pitch)
3073 {
3074  m_Protocol->SendSoundEffect(a_SoundName, a_Position, a_Volume, a_Pitch);
3075 }
3076 
3077 
3078 
3079 
3080 
3081 void cClientHandle::SendSoundParticleEffect(const EffectID a_EffectID, Vector3i a_Source, int a_Data)
3082 {
3083  m_Protocol->SendSoundParticleEffect(a_EffectID, a_Source, a_Data);
3084 }
3085 
3086 
3087 
3088 
3089 
3091 {
3092  m_Protocol->SendSpawnEntity(a_Entity);
3093 }
3094 
3095 
3096 
3097 
3098 
3100 {
3101  m_Protocol->SendSpawnMob(a_Mob);
3102 }
3103 
3104 
3105 
3106 
3107 
3109 {
3110  m_Protocol->SendStatistics(a_Manager);
3111 }
3112 
3113 
3114 
3115 
3116 
3118 {
3119  m_Protocol->SendTabCompletionResults(a_Results);
3120 }
3121 
3122 
3123 
3124 
3125 
3127 {
3128  m_Protocol->SendThunderbolt(a_BlockPos);
3129 }
3130 
3131 
3132 
3133 
3134 
3135 void cClientHandle::SendTitleTimes(int a_FadeInTicks, int a_DisplayTicks, int a_FadeOutTicks)
3136 {
3137  m_Protocol->SendTitleTimes(a_FadeInTicks, a_DisplayTicks, a_FadeOutTicks);
3138 }
3139 
3140 
3141 
3142 
3143 
3144 void cClientHandle::SendTimeUpdate(const cTickTimeLong a_WorldAge, const cTickTimeLong a_WorldDate, const bool a_DoDaylightCycle)
3145 {
3146  m_Protocol->SendTimeUpdate(a_WorldAge, a_WorldDate, a_DoDaylightCycle);
3147 }
3148 
3149 
3150 
3151 
3152 
3153 void cClientHandle::SendUnloadChunk(int a_ChunkX, int a_ChunkZ)
3154 {
3155  // Remove the chunk from the list of chunks sent to the client:
3156  {
3157  cCSLock Lock(m_CSChunkLists);
3158  m_SentChunks.remove(cChunkCoords(a_ChunkX, a_ChunkZ));
3159  }
3160 
3161  m_Protocol->SendUnloadChunk(a_ChunkX, a_ChunkZ);
3162 }
3163 
3164 
3165 
3166 
3167 
3169 {
3170  m_Protocol->SendUpdateBlockEntity(a_BlockEntity);
3171 }
3172 
3173 
3174 
3175 
3176 
3178  Vector3i a_BlockPos,
3179  const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4
3180 )
3181 {
3182  m_Protocol->SendUpdateSign(
3183  a_BlockPos,
3184  a_Line1, a_Line2, a_Line3, a_Line4
3185  );
3186 }
3187 
3188 
3189 
3190 
3191 
3193 {
3194  m_Protocol->SendUnlockRecipe(a_RecipeId);
3195 }
3196 
3197 
3198 
3199 
3200 
3202 {
3203  m_Protocol->SendInitRecipes(a_RecipeId);
3204 }
3205 
3206 
3207 
3208 
3209 
3211 {
3212  auto * Window = m_Player->GetWindow();
3213  if (Window == nullptr)
3214  {
3215  return;
3216  }
3217 
3218  if (Window->GetWindowType() == cWindow::wtInventory)
3219  {
3220  static_cast<cInventoryWindow *>(Window)->LoadRecipe(*m_Player, a_RecipeId);
3221  }
3222  else if (Window->GetWindowType() == cWindow::wtWorkbench)
3223  {
3224  static_cast<cCraftingWindow *>(Window)->LoadRecipe(*m_Player, a_RecipeId);
3225  }
3226 }
3227 
3228 
3229 
3230 
3231 
3233 {
3234  m_Protocol->SendWeather(a_Weather);
3235 }
3236 
3237 
3238 
3239 
3240 
3242 {
3243  m_Protocol->SendWholeInventory(a_Window);
3244 }
3245 
3246 
3247 
3248 
3249 
3251 {
3252  m_Protocol->SendWindowClose(a_Window);
3253 }
3254 
3255 
3256 
3257 
3258 
3260 {
3261  m_Protocol->SendWindowOpen(a_Window);
3262 }
3263 
3264 
3265 
3266 
3267 
3268 void cClientHandle::SendWindowProperty(const cWindow & a_Window, size_t a_Property, short a_Value)
3269 {
3270  m_Protocol->SendWindowProperty(a_Window, a_Property, a_Value);
3271 }
3272 
3273 
3274 
3275 
3276 
3278 {
3279  return m_Username;
3280 }
3281 
3282 
3283 
3284 
3285 
3287 {
3288  m_Username = std::move(a_Username);
3289 }
3290 
3291 
3292 
3293 
3294 
3295 void cClientHandle::SetViewDistance(int a_ViewDistance)
3296 {
3297  m_RequestedViewDistance = a_ViewDistance;
3298  LOGD("%s is requesting ViewDistance of %d!", GetUsername().c_str(), m_RequestedViewDistance);
3299 
3300  cWorld * world = m_Player->GetWorld();
3301  if (world != nullptr)
3302  {
3303  // Set the current view distance based on the requested VD and world max VD:
3305 
3306  // Restart chunk streaming to respond to new view distance:
3307  m_LastStreamedChunkX = std::numeric_limits<decltype(m_LastStreamedChunkX)>::max();
3308  m_LastStreamedChunkZ = std::numeric_limits<decltype(m_LastStreamedChunkZ)>::max();
3309  }
3310 }
3311 
3312 
3313 
3314 
3315 
3316 bool cClientHandle::HasPluginChannel(const AString & a_PluginChannel)
3317 {
3318  return (m_PluginChannels.find(a_PluginChannel) != m_PluginChannels.end());
3319 }
3320 
3321 
3322 
3323 
3324 
3325 bool cClientHandle::WantsSendChunk(int a_ChunkX, int a_ChunkZ)
3326 {
3327  if (m_State >= csDestroyed)
3328  {
3329  return false;
3330  }
3331 
3332  cCSLock Lock(m_CSChunkLists);
3333  return m_ChunksToSend.find(cChunkCoords(a_ChunkX, a_ChunkZ)) != m_ChunksToSend.end();
3334 }
3335 
3336 
3337 
3338 
3339 
3340 void cClientHandle::AddWantedChunk(int a_ChunkX, int a_ChunkZ)
3341 {
3342  if (m_State >= csDestroyed)
3343  {
3344  return;
3345  }
3346 
3347  LOGD("Adding chunk [%d, %d] to wanted chunks for client %p", a_ChunkX, a_ChunkZ, static_cast<void *>(this));
3348  cCSLock Lock(m_CSChunkLists);
3349  if (m_ChunksToSend.find(cChunkCoords(a_ChunkX, a_ChunkZ)) == m_ChunksToSend.end())
3350  {
3351  m_ChunksToSend.emplace(a_ChunkX, a_ChunkZ);
3352  }
3353 }
3354 
3355 
3356 
3357 
3358 
3360 {
3361  // Too much data in the incoming queue, the server is probably too busy, kick the client:
3362  LOGERROR("Too much data in queue for client \"%s\" @ %s, kicking them.", m_Username, m_IPString);
3363  SendDisconnect("The server is busy; please try again later.");
3364 }
3365 
3366 
3367 
3368 
3369 
3371 {
3372  LOGERROR("Unknown packet type 0x%x from client \"%s\" @ %s", a_PacketType, m_Username, m_IPString);
3373 
3374  SendDisconnect(fmt::format(FMT_STRING("Unknown [C->S] PacketType: 0x{:x}"), a_PacketType));
3375 }
3376 
3377 
3378 
3379 
3380 
3382 {
3383  LOGERROR("Protocol error while parsing packet type 0x%02x; disconnecting client \"%s\"", a_PacketType, m_Username);
3384  SendDisconnect("Protocol error");
3385 }
3386 
3387 
3388 
3389 
3390 
3392 {
3393  // The socket has been closed for any reason
3394  /*
3395  LOGD("SocketClosed for client %s @ %s (%p), state = %d, m_Player = %p",
3396  m_Username.c_str(), m_IPString.c_str(), static_cast<void *>(this), m_State.load(), static_cast<void *>(m_Player)
3397  );
3398  //*/
3399 
3400  // Log into console, unless it's a client ping:
3401  if (!m_Username.empty())
3402  {
3403  LOGD("Client %s @ %s disconnected", m_Username.c_str(), m_IPString.c_str());
3404  cRoot::Get()->GetPluginManager()->CallHookDisconnect(*this, "Player disconnected");
3405  }
3406 
3407  // Queue self for destruction:
3408  Destroy();
3409 }
3410 
3411 
3412 
3413 
3414 
3416 {
3417  cCSLock Lock(m_CSState);
3418  if (a_NewState <= m_State)
3419  {
3420  return false; // Can only advance the state machine
3421  }
3422  m_State = a_NewState;
3423  return true;
3424 }
3425 
3426 
3427 
3428 
3429 
3431 {
3432  m_Link = a_Link;
3433 }
3434 
3435 
3436 
3437 
3438 
3439 void cClientHandle::OnReceivedData(const char * a_Data, size_t a_Length)
3440 {
3441  // Reset the timeout:
3443 
3444  // Queue the incoming data to be processed in the tick thread:
3445  cCSLock Lock(m_CSIncomingData);
3446  m_IncomingData.append(reinterpret_cast<const std::byte *>(a_Data), a_Length);
3447 }
3448 
3449 
3450 
3451 
3452 
3454 {
3455  /*
3456  LOGD("Client socket for %s @ %s has been closed.",
3457  m_Username.c_str(), m_IPString.c_str()
3458  );
3459  //*/
3460  SocketClosed();
3461 }
3462 
3463 
3464 
3465 
3466 
3467 void cClientHandle::OnError(int a_ErrorCode, const AString & a_ErrorMsg)
3468 {
3469  LOGD("An error has occurred on client link for %s @ %s: %d (%s). Client disconnected.",
3470  m_Username.c_str(), m_IPString.c_str(), a_ErrorCode, a_ErrorMsg.c_str()
3471  );
3472  SocketClosed();
3473 }
AString ItemToString(const cItem &a_Item)
Translates a full item into a string.
Definition: BlockType.cpp:243
AString ItemToFullString(const cItem &a_Item)
Translates a full item into a fully-specified string (including meta and count).
Definition: BlockType.cpp:261
@ E_BLOCK_AIR
Definition: BlockType.h:10
@ E_BLOCK_FIRE
Definition: BlockType.h:61
@ E_ITEM_GOLDEN_APPLE
Definition: BlockType.h:366
@ E_ITEM_CHORUS_FRUIT
Definition: BlockType.h:479
@ E_ITEM_BOW
Definition: BlockType.h:305
std::vector< sSetBlock > sSetBlockVector
Definition: ChunkDef.h:441
unsigned char NIBBLETYPE
The datatype used by nibbledata (meta, light, skylight)
Definition: ChunkDef.h:44
std::list< cChunkCoords > cChunkCoordsList
Definition: ChunkDef.h:443
unsigned char BLOCKTYPE
The datatype used by blockdata.
Definition: ChunkDef.h:41
#define MAX_CHAT_MSG_LENGTH
Maximum number of bytes that a chat message sent by a player may consist of.
#define MAX_CHUNKS_STREAMED_PER_TICK
Maximum number of chunks to stream per tick.
#define MAX_BLOCK_CHANGE_INTERACTIONS
Maximum number of block change interactions a player can perform per tick - exceeding this causes a k...
#define MAX_EXPLOSIONS_PER_TICK
Maximum number of explosions to send this tick, server will start dropping if exceeded.
void AddFaceDirection(int &a_BlockX, int &a_BlockY, int &a_BlockZ, eBlockFace a_BlockFace, bool a_bInverse)
Modifies the specified coords so that they point to the block adjacent to the one specified through i...
Definition: Defines.cpp:378
const char * ClickActionToString(int a_ClickAction)
Returns a textual representation of the click action.
Definition: Defines.cpp:10
eWeather
Definition: Defines.h:160
T Diff(T a_Val1, T a_Val2)
Definition: Defines.h:617
BossBarDivisionType
Definition: Defines.h:443
EntityAnimation
Definition: Defines.h:458
eChatType
Definition: Defines.h:149
@ ctAboveActionBar
Definition: Defines.h:152
@ ctChatBox
Definition: Defines.h:150
@ ctSystem
Definition: Defines.h:151
@ DIG_STATUS_SWAP_ITEM_IN_HAND
Definition: Defines.h:73
@ DIG_STATUS_CANCELLED
Definition: Defines.h:68
@ DIG_STATUS_SHOOT_EAT
Definition: Defines.h:72
@ DIG_STATUS_DROP_STACK
Definition: Defines.h:70
@ DIG_STATUS_STARTED
Definition: Defines.h:67
@ DIG_STATUS_DROP_HELD
Definition: Defines.h:71
@ DIG_STATUS_FINISHED
Definition: Defines.h:69
eDimension
Dimension of a world.
Definition: Defines.h:231
@ dimNether
Definition: Defines.h:232
@ dimOverworld
Definition: Defines.h:233
eGameMode
Definition: Defines.h:125
eClickAction
Individual actions sent in the WindowClick packet.
Definition: Defines.h:82
eMessageType
Definition: Defines.h:352
@ mtWarning
Definition: Defines.h:360
@ mtPrivateMessage
Definition: Defines.h:363
@ mtJoin
Definition: Defines.h:364
@ mtMaxPlusOne
Definition: Defines.h:366
@ mtSuccess
Definition: Defines.h:359
@ mtCustom
Definition: Defines.h:356
@ mtInformation
Definition: Defines.h:358
@ mtDeath
Definition: Defines.h:362
@ mtLeave
Definition: Defines.h:365
@ mtFatal
Definition: Defines.h:361
@ mtFailure
Definition: Defines.h:357
eBlockFace
Block face constants, used in PlayerDigging and PlayerBlockPlacement packets and bbox collision calc.
Definition: Defines.h:38
@ BLOCK_FACE_NONE
Definition: Defines.h:39
BossBarColor
Definition: Defines.h:428
EffectID
Definition: EffectID.h:6
@ PARTICLE_BLOCK_BREAK
MTRand & GetRandomProvider()
Returns the current thread's random number source.
Definition: FastRandom.cpp:12
std::chrono::duration< signed long long int, cTickTime::period > cTickTimeLong
Definition: Globals.h:367
unsigned int UInt32
Definition: Globals.h:157
signed short Int16
Definition: Globals.h:153
std::basic_string_view< std::byte > ContiguousByteBufferView
Definition: Globals.h:376
#define FAST_FLOOR_DIV(x, div)
Faster than (int)floorf((float)x / (float)div)
Definition: Globals.h:238
T Clamp(T a_Value, T a_Min, T a_Max)
Clamp X to the specified range.
Definition: Globals.h:336
unsigned char UInt8
Definition: Globals.h:159
#define ASSERT(x)
Definition: Globals.h:276
#define UNUSED
Definition: Globals.h:72
std::enable_if< std::is_arithmetic< T >::value, C >::type FloorC(T a_Value)
Floors a value, then casts it to C (an int by default).
Definition: Globals.h:347
unsigned char Byte
Definition: Globals.h:161
#define FLOGD
Definition: LoggerSimple.h:91
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 LOGD
Definition: LoggerSimple.h:83
void LOGINFO(std::string_view a_Format, const Args &... args)
Definition: LoggerSimple.h:61
std::shared_ptr< cTCPLink > cTCPLinkPtr
Definition: Network.h:25
BlockType
Definition: BlockTypes.h:4
AString StripColorCodes(const AString &a_Message)
Removes all control codes used by MC for colors and styles.
std::vector< AString > AStringVector
Definition: StringUtils.h:12
std::string AString
Definition: StringUtils.h:11
Vector3< double > Vector3d
Definition: Vector3.h:485
bool IsSword(short a_ItemType)
Definition: Defines.cpp:460
Utilities to allow casting a cWorld to one of its interfaces without including World....
Definition: OpaqueWorld.h:13
unsigned char Distance(const BlockState Block)
Definition: FastNBT.h:132
bool CallHookPlayerRightClick(cPlayer &a_Player, Vector3i a_BlockPos, eBlockFace a_BlockFace, Vector3i a_CursorPos)
bool CallHookPlayerShooting(cPlayer &a_Player)
bool CallHookPlayerEating(cPlayer &a_Player)
static cPluginManager * Get(void)
Returns the instance of the Plugin Manager (there is only ever one)
bool CallHookPlayerUsingItem(cPlayer &a_Player, Vector3i a_BlockPos, eBlockFace a_BlockFace, Vector3i a_CursorPos)
bool CallHookPlayerTossingItem(cPlayer &a_Player)
bool CallHookPlayerLeftClick(cPlayer &a_Player, Vector3i a_BlockPos, eBlockFace a_BlockFace, char a_Status)
bool CallHookPlayerSpawned(cPlayer &a_Player)
void TabCompleteCommand(const AString &a_Text, AStringVector &a_Results, cPlayer *a_Player)
Appends all commands beginning with a_Text (case-insensitive) into a_Results.
bool CallHookPlayerUsingBlock(cPlayer &a_Player, Vector3i a_BlockPos, eBlockFace a_BlockFace, Vector3i a_CursorPos, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
bool CallHookPlayerUsedBlock(cPlayer &a_Player, Vector3i a_BlockPos, eBlockFace a_BlockFace, Vector3i a_CursorPos, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
bool CallHookPlayerBrokenBlock(cPlayer &a_Player, Vector3i a_BlockPos, eBlockFace a_BlockFace, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
bool CallHookDisconnect(cClientHandle &a_Client, const AString &a_Reason)
bool CallHookPluginMessage(cClientHandle &a_Client, const AString &a_Channel, ContiguousByteBufferView a_Message)
bool CallHookPlayerUsedItem(cPlayer &a_Player, Vector3i a_BlockPos, eBlockFace a_BlockFace, Vector3i a_CursorPos)
bool SetPrimaryEffect(cEntityEffect::eType a_Effect)
Sets the primary effect.
cEntityEffect::eType GetPrimaryEffect(void) const
Definition: BeaconEntity.h:48
bool SetSecondaryEffect(cEntityEffect::eType a_Effect)
Sets the secondary effect.
static bool IsUseableBySpectator(BLOCKTYPE Block)
Can a spectator interact with this block?
Definition: BlockInfo.cpp:1134
static bool IsClickedThrough(BLOCKTYPE a_Block)
Does the client pretend the block doesn't exist when clicking? For example, digging a fire will hit t...
Definition: BlockInfo.cpp:720
static bool IsOneHitDig(BLOCKTYPE Block)
Is a block destroyed after a single hit? Warning: IsOneHitDig does not take into account enchantments...
Definition: BlockInfo.cpp:731
static void VacateBed(cChunkInterface &a_ChunkInterface, cPlayer &a_Player)
Definition: BlockBed.h:43
virtual void OnDigging(cChunkInterface &a_ChunkInterface, cWorldInterface &a_WorldInterface, cPlayer &a_Player, const Vector3i a_BlockPos) const
Called when the player starts digging the block.
Definition: BlockHandler.h:81
static const cBlockHandler & For(BLOCKTYPE a_BlockType)
static const char * LightBlue
Definition: ChatColor.h:28
static const char * Yellow
Definition: ChatColor.h:31
static const char * Red
Definition: ChatColor.h:21
static const char * White
Definition: ChatColor.h:32
static const char * Gray
Definition: ChatColor.h:25
static const char * Rose
Definition: ChatColor.h:29
static const char * Delimiter
Definition: ChatColor.h:12
static const char * Italic
Definition: ChatColor.h:40
static const char * Green
Definition: ChatColor.h:19
bool IsValid(void) const
Returns true iff the chunk block data is valid (loaded / generated)
Definition: Chunk.h:58
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
static bool IsValidHeight(Vector3i a_BlockPosition)
Validates a height-coordinate.
Definition: ChunkDef.h:185
static void BlockToChunk(int a_X, int a_Z, int &a_ChunkX, int &a_ChunkZ)
Converts absolute block coords to chunk coords:
Definition: ChunkDef.h:210
static const int Width
Definition: ChunkDef.h:124
static const int Height
Definition: ChunkDef.h:125
Priority
Tag indicating urgency of chunk to be sent.
Definition: ChunkSender.h:64
void SendSetRawSubTitle(const AString &a_SubTitle)
void FinishAuthenticate()
Finish logging the user in after authenticating.
void HandleUnmount(void)
void SendHideTitle(void)
bool CheckBlockInteractionsRate(void)
Returns true if the rate block interactions is within a reasonable limit (bot protection)
void StreamNextChunks()
Sends a set number of new chunks to the player on every invocation, until all chunks in the view dist...
static Vector3i s_IllegalPosition
Definition: ClientHandle.h:545
void SendSetSubTitle(const cCompositeChat &a_SubTitle)
void HandleCommandBlockBlockChange(Vector3i a_BlockPos, const AString &a_NewCommand)
Called when the protocol receives a message, indicating that the player set a new command in the comm...
int GetUniqueID(void) const
Definition: ClientHandle.h:270
cChannels m_PluginChannels
The plugin channels that the client has registered.
Definition: ClientHandle.h:563
void SendHeldItemChange(int a_ItemIndex)
cPlayer * m_Player
A pointer to a World-owned player object, created in FinishAuthenticate when authentication succeeds.
Definition: ClientHandle.h:477
int m_NumBlockChangeInteractionsThisTick
Number of place or break interactions this tick.
Definition: ClientHandle.h:541
Vector3i m_LastPlacedSign
The positions from the last sign that the player placed.
Definition: ClientHandle.h:560
void HandleLeftClick(Vector3i a_BlockPos, eBlockFace a_BlockFace, UInt8 a_Status)
void HandleCommandBlockEntityChange(UInt32 a_EntityID, const AString &a_NewCommand)
Called when the protocol receives a message, indicating that the player set a new command in the comm...
void HandleRespawn(void)
cForgeHandshake m_ForgeHandshake
Forge handshake state machine.
Definition: ClientHandle.h:434
void SendSetTitle(const cCompositeChat &a_Title)
void SetViewDistance(int a_ViewDistance)
Sets the maximal view distance.
void SetUsername(AString &&a_Username)
void HandleEnchantItem(UInt8 a_WindowID, UInt8 a_Enchantment)
Called when the player enchants an Item in the Enchanting table UI.
void SendParticleEffect(const AString &a_ParticleName, Vector3f a_Source, Vector3f a_Offset, float a_ParticleData, int a_ParticleAmount)
cCriticalSection m_CSOutgoingData
Protects m_OutgoingData against multithreaded access.
Definition: ClientHandle.h:466
void SocketClosed(void)
Called when the network socket has been closed.
void SendLeashEntity(const cEntity &a_Entity, const cEntity &a_EntityLeashedTo)
AString m_Username
Definition: ClientHandle.h:447
int m_LastStreamedChunkX
Definition: ClientHandle.h:491
Json::Value m_Properties
Definition: ClientHandle.h:449
void Kick(const AString &a_Reason)
void SendChatRaw(const AString &a_MessageRaw, eChatType a_Type)
void HandleChat(const AString &a_Message)
Called when the protocol detects a chat packet.
void UnregisterPluginChannels(const AStringVector &a_ChannelList)
Removes all of the channels from the list of current plugin channels.
void SendEntityProperties(const cEntity &a_Entity)
void SendEntityHeadLook(const cEntity &a_Entity)
void SendPlayerListUpdateDisplayName(const cPlayer &a_Player, const AString &a_CustomName)
UInt32 m_PingID
ID of the last ping request sent to the client.
Definition: ClientHandle.h:504
static AString FormatMessageType(bool ShouldAppendChatPrefixes, eMessageType a_ChatPrefix, const AString &a_AdditionalData)
Formats the type of message with the proper color and prefix for sending to the client.
void SendDestroyEntity(const cEntity &a_Entity)
void SendScoreboardObjective(const AString &a_Name, const AString &a_DisplayName, Byte a_Mode)
Vector3i m_BlockDigAnimPos
Definition: ClientHandle.h:512
void HandleResourcePack(UInt8 a_Status)
void HandleAnvilItemName(const AString &a_ItemName)
Called when the protocol receives a MC|ItemName plugin message, indicating that the player named an i...
int m_CurrentViewDistance
The actual view distance used, the minimum of client's requested view distance and world's max view d...
Definition: ClientHandle.h:440
void SendChatAboveActionBar(const AString &a_Message, eMessageType a_ChatPrefix, const AString &a_AdditionalData="")
void SendWindowProperty(const cWindow &a_Window, size_t a_Property, short a_Value)
static float FASTBREAK_PERCENTAGE
The percentage how much a block has to be broken.
Definition: ClientHandle.h:65
void SendThunderbolt(Vector3i a_BlockPos)
void SendInitRecipes(UInt32 a_RecipeId)
Send already known recipes without notification but visible in the recipe book.
cCriticalSection m_CSState
Definition: ClientHandle.h:528
void SendPlayerListUpdateGameMode(const cPlayer &a_Player)
void SendEntityPosition(const cEntity &a_Entity)
std::atomic< int > m_TicksSinceLastPacket
Number of ticks since the last network packet was received (increased in Tick(), reset in OnReceivedD...
Definition: ClientHandle.h:495
void SendUnloadChunk(int a_ChunkX, int a_ChunkZ)
void SendSpawnMob(const cMonster &a_Mob)
void RegisterPluginChannels(const AStringVector &a_ChannelList)
Adds all of the channels to the list of current plugin channels.
void SendSoundParticleEffect(const EffectID a_EffectID, Vector3i a_Source, int a_Data)
virtual void OnLinkCreated(cTCPLinkPtr a_Link) override
Called when the cTCPLink for the connection is created.
void SendPlayerPermissionLevel(void)
std::unordered_set< cChunkCoords, cChunkCoordsHash > m_ChunksToSend
Definition: ClientHandle.h:453
void PacketUnknown(UInt32 a_PacketType)
void SendBlockChange(Vector3i a_BlockPos, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
void SendInventorySlot(char a_WindowID, short a_SlotNum, const cItem &a_Item)
void SendCameraSetTo(const cEntity &a_Entity)
void HandleWindowClick(UInt8 a_WindowID, Int16 a_SlotNum, eClickAction a_ClickAction, const cItem &a_HeldItem)
void SendScoreUpdate(const AString &a_Objective, const AString &a_Player, cObjective::Score a_Score, Byte a_Mode)
void HandleLeaveBed()
Handles a player exiting his bed.
int m_NumExplosionsThisTick
Number of explosions sent this tick.
Definition: ClientHandle.h:538
float m_BreakProgress
The fraction between 0 and 1 (or above), of how far through mining the currently mined block is.
Definition: ClientHandle.h:577
void SendTimeUpdate(cTickTimeLong a_WorldAge, cTickTimeLong a_WorldDate, bool a_DoDaylightCycle)
void ServerTick(float a_Dt)
Called while the client is being ticked from the cServer object.
void HandlePing(void)
bool IsDestroyed(void) const
Definition: ClientHandle.h:148
void SendUpdateSign(Vector3i a_BlockPos, const AString &a_Line1, const AString &a_Line2, const AString &a_Line3, const AString &a_Line4)
void StreamChunk(int a_ChunkX, int a_ChunkZ, cChunkSender::Priority a_Priority)
Adds a single chunk to be streamed to the client; used by StreamChunks()
UInt32 m_ProtocolVersion
The version of the protocol that the client is talking, or 0 if unknown.
Definition: ClientHandle.h:569
void SendBossBarRemove(UInt32 a_UniqueID)
void HandleKeepAlive(UInt32 a_KeepAliveID)
void PacketError(UInt32 a_PacketType)
cMultiVersionProtocol m_Protocol
Definition: ClientHandle.h:456
void SendPluginMessage(const AString &a_Channel, std::string_view a_Message)
void HandleBlockDigStarted(Vector3i a_BlockPos, eBlockFace a_BlockFace)
Handles the DIG_STARTED dig packet:
void SendUnleashEntity(const cEntity &a_Entity)
AStringVector BreakApartPluginChannels(ContiguousByteBufferView a_PluginChannels)
Converts the protocol-formatted channel list (NUL-separated) into a proper string vector.
bool m_ProxyConnection
True if player connected from a proxy (Bungee / Velocity)
Definition: ClientHandle.h:486
void SendPlayerAbilities(void)
void HandlePlayerLook(float a_Rotation, float a_Pitch, bool a_IsOnGround)
std::unordered_set< cChunkCoords, cChunkCoordsHash > m_LoadedChunks
Definition: ClientHandle.h:452
void HandleRightClick(Vector3i a_BlockPos, eBlockFace a_BlockFace, Vector3i a_Cursor, bool a_UsedMainHand)
void SendSpawnEntity(const cEntity &a_Entity)
void HandlePluginMessage(const AString &a_Channel, ContiguousByteBufferView a_Message)
ContiguousByteBuffer m_IncomingData
Queue for the incoming data received on the link until it is processed in ProcessProtocolIn().
Definition: ClientHandle.h:463
bool HandleHandshake(const AString &a_Username)
Called when the protocol handshake has been received (for protocol versions that support it; otherwis...
void SendResetTitle(void)
void SendWindowOpen(const cWindow &a_Window)
std::atomic< eState > m_State
The current (networking) state of the client.
Definition: ClientHandle.h:535
int m_LastStreamedChunkZ
Definition: ClientHandle.h:492
void ProxyInit(const AString &a_IPString, const cUUID &a_UUID)
Function to mark bungee / proxy connection on this client, and to add proxy-related data.
void SendWeather(eWeather a_Weather)
void SendPlayerListHeaderFooter(const cCompositeChat &a_Header, const cCompositeChat &a_Footer)
cTCPLinkPtr m_Link
The link that is used for network communication.
Definition: ClientHandle.h:573
void HandleUseEntity(UInt32 a_TargetEntityID, bool a_IsLeftClick)
cCriticalSection m_CSIncomingData
Protects m_IncomingData against multithreaded access.
Definition: ClientHandle.h:459
void SendGameMode(eGameMode a_GameMode)
virtual void OnRemoteClosed(void) override
Called when the remote end closes the connection.
void HandleUpdateSign(Vector3i a_BlockPos, const AString &a_Line1, const AString &a_Line2, const AString &a_Line3, const AString &a_Line4)
static const int MIN_VIEW_DISTANCE
Definition: ClientHandle.h:60
void SetProperties(const Json::Value &a_Properties)
Sets the player's properties, such as skin image and signature.
Definition: ClientHandle.h:93
static bool IsUUIDOnline(const cUUID &a_UUID)
Returns true if the UUID is generated by online auth, false if it is an offline-generated UUID.
void SendWindowClose(const cWindow &a_Window)
std::chrono::milliseconds m_TimeSinceLastUnloadCheck
The time since UnloadOutOfRangeChunks was last called.
Definition: ClientHandle.h:498
bool IsWithinReach(Vector3i a_Position) const
Returns whether the player could in fact reach the position they're attempting to interact with.
void SendBlockBreakAnim(UInt32 a_EntityID, Vector3i a_BlockPos, char a_Stage)
std::chrono::steady_clock::duration m_Ping
Duration of the last completed client ping.
Definition: ClientHandle.h:501
void HandleSteerVehicle(float Forward, float Sideways)
void SendSoundEffect(const AString &a_SoundName, double a_X, double a_Y, double a_Z, float a_Volume, float a_Pitch)
void SendEntityMetadata(const cEntity &a_Entity)
void SendPaintingSpawn(const cPainting &a_Painting)
void SendRemoveEntityEffect(const cEntity &a_Entity, int a_EffectID)
void UnloadOutOfRangeChunks(void)
Remove all loaded chunks that are no longer in range.
void HandleStartElytraFlight()
Handles a player starting elytra flight while falling.
void HandleUseItem(bool a_UsedMainHand)
void HandleSprint(bool a_IsSprinting)
Handles a player sprinting or slowing back down.
cClientHandle(const AString &a_IPString, int a_ViewDistance)
Creates a new client with the specified IP address in its description and the specified initial view ...
void SendTitleTimes(int a_FadeInTicks, int a_DisplayTicks, int a_FadeOutTicks)
void ProcessProtocolIn(void)
Processes the data in the network input buffer.
void SendPlayerListUpdatePing()
void SendPlayerSpawn(const cPlayer &a_Player)
const AString & GetUsername(void) const
void SendTabCompletionResults(const AStringVector &a_Results)
void HandlePlayerMove(Vector3d a_Pos, bool a_IsOnGround)
Verifies and sets player position, performing relevant checks.
void ProcessProtocolOut()
Flushes all buffered outgoing data to the network.
void SendPlayerMoveLook(void)
void SendEntityVelocity(const cEntity &a_Entity)
@ csConnected
The client has just connected, waiting for their handshake / login.
Definition: ClientHandle.h:520
@ csAuthenticating
The client has logged in, waiting for external authentication.
Definition: ClientHandle.h:521
@ csDestroyed
The client has been destroyed, the destructor is to be called from the owner thread.
Definition: ClientHandle.h:524
@ csPlaying
Normal gameplay.
Definition: ClientHandle.h:523
@ csDownloadingWorld
The client is waiting for chunks, we're waiting for the loader to provide and send them.
Definition: ClientHandle.h:522
const AString & GetIPString(void) const
Definition: ClientHandle.h:72
static cUUID GenerateOfflineUUID(const AString &a_Username)
Generates an UUID based on the player name provided.
void SendStatistics(const StatisticsManager &a_Manager)
void HandleTabCompletion(const AString &a_Text)
void SendBlockChanges(int a_ChunkX, int a_ChunkZ, const sSetBlockVector &a_Changes)
ContiguousByteBuffer m_OutgoingData
Buffer for storing outgoing data from any thread; will get sent in ProcessProtocolOut() at the end of...
Definition: ClientHandle.h:470
void SendBossBarUpdateStyle(UInt32 a_UniqueID, BossBarColor a_Color, BossBarDivisionType a_DivisionType)
void SendEntityEquipment(const cEntity &a_Entity, short a_SlotNum, const cItem &a_Item)
void HandlePlayerAbilities(bool a_IsFlying, float FlyingSpeed, float WalkingSpeed)
void HandleNPCTrade(int a_SlotNum)
Called when the protocol receives a MC|TrSel packet, indicating that the player used a trade in the N...
AString m_IPString
Definition: ClientHandle.h:445
Vector3i m_LastDigBlockPos
Definition: ClientHandle.h:516
void HandleSpectate(const cUUID &a_PlayerUUID)
void HandleBlockDigFinished(Vector3i a_BlockPos, eBlockFace a_BlockFace)
Handles the DIG_FINISHED dig packet:
bool m_HasStartedDigging
Definition: ClientHandle.h:515
void SetIPString(const AString &a_IPString)
Sets the IP string that the client is using.
Definition: ClientHandle.h:76
void SendEntityEffect(const cEntity &a_Entity, int a_EffectID, int a_Amplifier, int a_Duration)
cChunkCoords m_CachedSentChunk
This is an optimization which saves you an iteration of m_SentChunks if you just want to know whether...
Definition: ClientHandle.h:484
void SendBlockAction(Vector3i a_BlockPos, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType)
void SendBossBarUpdateHealth(UInt32 a_UniqueID, float a_FractionFilled)
void HandleBeaconSelection(unsigned a_PrimaryEffect, unsigned a_SecondaryEffect)
Called when the protocol receives a MC|Beacon plugin message, indicating that the player set an effec...
int m_RequestedViewDistance
The requested view distance from the player.
Definition: ClientHandle.h:443
bool CheckMultiLogin(const AString &a_Username)
Kicks the client if the same username is already logged in.
cUUID m_UUID
Contains the UUID used by Mojang to identify the player's account.
Definition: ClientHandle.h:551
void Destroy(void)
void RemoveFromWorld(void)
Called when the player moves into a different world.
void SendDetachEntity(const cEntity &a_Entity, const cEntity &a_PreviousVehicle)
cChunkCoordsList m_SentChunks
Definition: ClientHandle.h:454
void SendPlayerListAddPlayer(const cPlayer &a_Player)
void SendBossBarUpdateFlags(UInt32 a_UniqueID, bool a_DarkenSky, bool a_PlayEndMusic, bool a_CreateFog)
void SendExplosion(Vector3f a_Position, float a_Power)
void SendMapData(const cMap &a_Map, int a_DataStartX, int a_DataStartY)
void HandleWindowClose(UInt8 a_WindowID)
cCriticalSection m_CSChunkLists
Definition: ClientHandle.h:451
void SendDisconnect(const AString &a_Reason)
void SendRespawn(eDimension a_Dimension, bool a_IsRespawningFromDeath)
virtual ~cClientHandle() override
void SendEntityLook(const cEntity &a_Entity)
static AString FormatChatPrefix(bool ShouldAppendChatPrefixes, const AString &a_ChatPrefixS, const AString &m_Color1, const AString &m_Color2)
void HandleCrouch(bool a_IsCrouching)
Handles a player sneaking or unsneaking.
cPlayer * GetPlayer(void)
Definition: ClientHandle.h:78
bool IsPlayerChunkSent()
void PacketBufferFull(void)
void HandleSlotSelected(Int16 a_SlotNum)
int m_UniqueID
ID used for identification during authenticating.
Definition: ClientHandle.h:548
void HandlePlayerMoveLook(Vector3d a_Pos, float a_Rotation, float a_Pitch, bool a_IsOnGround)
void SendExperienceOrb(const cExpOrb &a_ExpOrb)
bool m_HasSentPlayerChunk
Set to true when the chunk where the player is is sent to the client.
Definition: ClientHandle.h:554
void SendCollectEntity(const cEntity &a_Collected, const cEntity &a_Collector, unsigned a_Count)
bool HandleLogin()
Called when the protocol has finished logging the user in.
virtual void OnReceivedData(const char *a_Data, size_t a_Length) override
Called when there's data incoming from the remote peer.
void SendEditSign(Vector3i a_BlockPos)
bool m_HasSentDC
True if a Disconnect packet has been sent in either direction.
Definition: ClientHandle.h:488
void SendBossBarAdd(UInt32 a_UniqueID, const cCompositeChat &a_Title, float a_FractionFilled, BossBarColor a_Color, BossBarDivisionType a_DivisionType, bool a_DarkenSky, bool a_PlayEndMusic, bool a_CreateFog)
void SendResourcePack(const AString &a_ResourcePackUrl)
void SendPlayerPosition(void)
void SetUUID(const cUUID &a_UUID)
Sets the player's UUID, as used by the protocol.
Definition: ClientHandle.h:86
void SendExperience(void)
void SendWholeInventory(const cWindow &a_Window)
void FinishDigAnimation()
The clients will receive a finished dig animation.
void Tick(std::chrono::milliseconds a_Dt)
Called while the client is being ticked from the world via its cPlayer object.
static int s_ClientCount
Definition: ClientHandle.h:543
void SendUnlockRecipe(UInt32 a_RecipeId)
Send a newly discovered recipe to show the notification and unlock in the recipe book.
void SendDisplayObjective(const AString &a_Objective, cScoreboard::eDisplaySlot a_Display)
void SendChat(const AString &a_Message, eMessageType a_ChatPrefix, const AString &a_AdditionalData="")
void SendSetRawTitle(const AString &a_Title)
void SendHealth(void)
void HandleCraftRecipe(UInt32 a_RecipeId)
Called when a recipe from the recipe book is selected.
bool HasPluginChannel(const AString &a_PluginChannel)
int m_BlockDigAnimSpeed
Definition: ClientHandle.h:511
bool BungeeAuthenticate()
Authenticates the specified user with the bungee proxy server.
bool SetState(eState a_NewState)
Called to update m_State.
void HandleOpenHorseInventory()
Handles a player opening his inventory while riding a horse.
void SendPlayerListRemovePlayer(const cPlayer &a_Player)
void SendData(ContiguousByteBufferView a_Data)
void SendChunkData(int a_ChunkX, int a_ChunkZ, ContiguousByteBufferView a_ChunkData)
void SendUpdateBlockEntity(cBlockEntity &a_BlockEntity)
void HandleAnimation(bool a_SwingMainHand)
Called when the protocol receives a (hand swing) animation packet.
int m_BlockDigAnimStage
Definition: ClientHandle.h:510
void SendChatSystem(const AString &a_Message, eMessageType a_ChatPrefix, const AString &a_AdditionalData="")
void SendEntityAnimation(const cEntity &a_Entity, EntityAnimation a_Animation)
bool WantsSendChunk(int a_ChunkX, int a_ChunkZ)
Returns true if the client wants the chunk specified to be sent (in m_ChunksToSend)
const Json::Value & GetProperties(void) const
Definition: ClientHandle.h:88
void SendAttachEntity(const cEntity &a_Entity, const cEntity &a_Vehicle)
void Authenticate(AString &&a_Name, const cUUID &a_UUID, Json::Value &&a_Properties)
Authenticates ourselves, called by cAuthenticator supplying player details from Mojang.
void HandleCreativeInventory(Int16 a_SlotNum, const cItem &a_HeldItem, eClickAction a_ClickAction)
Called when the client clicks the creative inventory window.
std::chrono::steady_clock::time_point m_PingStartTime
Time of the last ping request sent to the client.
Definition: ClientHandle.h:507
virtual void OnError(int a_ErrorCode, const AString &a_ErrorMsg) override
Called when an error is detected on the connection.
void AddWantedChunk(int a_ChunkX, int a_ChunkZ)
Adds the chunk specified to the list of chunks wanted for sending (m_ChunksToSend)
void SendBossBarUpdateTitle(UInt32 a_UniqueID, const cCompositeChat &a_Title)
Container for a single chat message composed of multiple functional parts.
Definition: CompositeChat.h:34
void UnderlineUrls(void)
Adds the "underline" style to each part that is an URL.
void ParseText(const AString &a_ParseText)
Parses text into various parts, adds those.
void AddTextPart(const AString &a_Message, const AString &a_Style="")
Adds a plain text part, with optional style.
Definition: Entity.h:76
void SetPitch(double a_Pitch)
Definition: Entity.cpp:2136
int GetChunkZ(void) const
Definition: Entity.h:208
void SteerVehicle(float a_Forward, float a_Sideways)
Definition: Entity.cpp:2243
void SetHeadYaw(double a_HeadYaw)
Definition: Entity.cpp:2102
void Detach(void)
Detaches from the currently attached entity, if any.
Definition: Entity.cpp:2052
void SetYaw(double a_Yaw)
Definition: Entity.cpp:2125
cChunk * GetParentChunk()
Returns the chunk responsible for ticking this entity.
Definition: Entity.h:541
int GetChunkX(void) const
Definition: Entity.h:207
virtual void TeleportToEntity(cEntity &a_Entity)
Teleports to the entity specified.
Definition: Entity.cpp:1944
double GetPosX(void) const
Definition: Entity.h:195
void SetPosition(double a_PosX, double a_PosY, double a_PosZ)
Definition: Entity.h:218
double GetPosZ(void) const
Definition: Entity.h:197
bool IsMinecart(void) const
Definition: Entity.h:165
bool Initialize(OwnedEntity a_Self, cWorld &a_EntityWorld)
Spawns the entity in the world; returns true if spawned, false if not (plugin disallowed).
Definition: Entity.cpp:111
UInt32 GetUniqueID(void) const
Definition: Entity.h:253
void TakeDamage(cEntity &a_Attacker)
Makes this pawn take damage from an attack by a_Attacker.
Definition: Entity.cpp:272
bool IsPawn(void) const
Definition: Entity.h:163
float GetHealth(void) const
Returns the health of this entity.
Definition: Entity.h:367
virtual void OnRightClicked(cPlayer &a_Player)
Called when the specified player right-clicks this entity.
Definition: Entity.h:524
const Vector3d & GetPosition(void) const
Exported in ManualBindings.
Definition: Entity.h:297
Vector3d GetLookVector(void) const
Definition: Entity.cpp:2267
cWorld * GetWorld(void) const
Definition: Entity.h:190
eType
All types of entity effects (numbers correspond to protocol / storage types)
Definition: EntityEffect.h:12
Definition: ExpOrb.h:13
@ mpHopper
Definition: Minecart.h:35
@ mpChest
Definition: Minecart.h:32
ePayload GetPayload(void) const
Definition: Minecart.h:47
Definition: Player.h:29
void SendMessage(const AString &a_Message)
Definition: Player.cpp:1182
void SetFlying(bool a_ShouldFly)
Starts or stops flying, broadcasting the state change.
Definition: Player.cpp:700
const AString & GetName(void) const
Definition: Player.cpp:1299
void UpdateMovementStats(const Vector3d &a_DeltaPos, bool a_PreviousIsOnGround)
Update movement-related statistics.
Definition: Player.cpp:2216
AString GetColor(void) const
Returns the full color code to use for this player, based on their rank.
Definition: Player.cpp:1651
bool IsFrozen()
Is the player frozen?
Definition: Player.cpp:1432
StatisticsManager & GetStatistics()
Return the associated statistic and achievement manager.
Definition: Player.h:237
int GetCurrentXp(void)
Gets the current experience.
Definition: Player.h:117
void CloseWindow(bool a_CanRefuse=true)
Closes the current window, resets current window to m_InventoryWindow.
Definition: Player.cpp:1144
void Respawn(void)
Definition: Player.cpp:936
const cItem & GetEquippedItem(void) const
Definition: Player.h:162
float GetXpPercentage(void) const
Gets the experience bar percentage - XpP.
Definition: Player.cpp:246
void SpectateEntity(cEntity *a_Target)
Spectates the target entity.
Definition: Player.cpp:1471
void AbortEating(void)
Aborts the current eating operation.
Definition: Player.cpp:551
void AddKnownItem(const cItem &a_Item)
Adds an Item to the list of known items.
Definition: Player.cpp:2708
bool IsGameModeSurvival(void) const
Returns true if the player is in Survival mode, either explicitly, or by inheriting from current worl...
Definition: Player.cpp:1034
void OpenHorseInventory()
Opens the inventory of any tame horse the player is riding.
Definition: Player.cpp:1904
int DeltaExperience(int a_Xp_delta)
Definition: Player.cpp:279
AString GetSuffix(void) const
Returns the player name suffix, may contain @ format directives.
Definition: Player.cpp:1676
int GetXpLevel(void) const
Gets the current level - XpLevel.
Definition: Player.cpp:237
const AString & GetLoadedWorldName() const
Definition: Player.h:429
virtual bool IsCrouched(void) const override
Definition: Player.cpp:2949
void TossEquippedItem(char a_Amount=1)
tosses the item in the selected hotbar slot
Definition: Player.cpp:1720
bool IsGameModeCreative(void) const
Returns true if the player is in Creative mode, either explicitly, or by inheriting from current worl...
Definition: Player.cpp:1025
virtual cItem GetOffHandEquipedItem(void) const override
Returns the currently offhand equipped item; empty item if none.
Definition: Player.h:602
cInventory & GetInventory(void)
Definition: Player.h:156
void AddFoodExhaustion(double a_Exhaustion)
Adds the specified exhaustion to m_FoodExhaustion.
Definition: Player.cpp:439
void PermuteEnchantmentSeed()
Permute the seed for enchanting related PRNGs, don't use this for other purposes.
Definition: Player.cpp:1576
bool IsGameModeSpectator(void) const
Returns true if the player is in Spectator mode, either explicitly, or by inheriting from current wor...
Definition: Player.cpp:1052
bool CanInstantlyMine(BLOCKTYPE a_Block)
Given tool, enchantments, status effects, and world position returns whether a_Block would be instant...
Definition: Player.cpp:2696
bool HasPermission(const AString &a_Permission) const
Definition: Player.cpp:1586
const std::set< UInt32 > & GetKnownRecipes() const
Gets the set of IDs for recipes this player has discovered.
Definition: Player.cpp:206
void SetElytraFlight(bool a_ShouldElytraFly)
Starts or stops elytra flight, if our current body stance permits, broadcasting the state change.
Definition: Player.cpp:682
cWindow * GetWindow(void)
Definition: Player.h:262
void StartEating(void)
Starts eating the currently equipped item.
Definition: Player.cpp:514
void SetCrouch(bool a_ShouldCrouch)
Starts or stops crouching, if our current body stance permits, broadcasting the state change.
Definition: Player.cpp:656
void SendBlocksAround(Vector3i a_BlockPos, int a_Range=1)
Sends the block in the specified range around the specified coord to the client as a block change pac...
Definition: Player.cpp:2325
void CloseWindowIfID(char a_WindowID, bool a_CanRefuse=true)
Closes the current window if it matches the specified ID, resets current window to m_InventoryWindow.
Definition: Player.cpp:1169
Vector3d GetEyePosition(void) const
Definition: Player.cpp:1001
bool IsSatiated(void) const
Returns true if the player is satiated, i.
Definition: Player.h:360
void SetSprint(bool a_ShouldSprint)
Starts or stops sprinting, if our current body stance permits, broadcasting the state change.
Definition: Player.cpp:729
AString GetPrefix(void) const
Returns the player name prefix, may contain @ format directives.
Definition: Player.cpp:1667
bool IsGameModeAdventure(void) const
Returns true if the player is in Adventure mode, either explicitly, or by inheriting from current wor...
Definition: Player.cpp:1043
void SetTouchGround(bool a_bTouchGround)
Definition: Player.cpp:351
static int XpForLevel(int a_Level)
Calculates the amount of XP needed for a given level Ref: https://minecraft.wiki/w/XP.
Definition: Player.cpp:215
virtual bool IsOnGround(void) const override
Returns whether the entity is on ground or not.
Definition: Player.h:604
float GetMiningProgressPerTick(BLOCKTYPE a_Block)
Returns the progress mined per tick for the block a_Block as a fraction (1 would be completely mined)...
Definition: Player.cpp:2673
void UseEquippedItem(short a_Damage=1)
Damage the player's equipped item by a_Damage, possibly less if the equipped item is enchanted.
Definition: Player.cpp:2033
This class represents the player's inventory The slots are divided into three areas:
Definition: Inventory.h:36
void SetEquippedSlotNum(int a_SlotNum)
Sets equiped item to the a_SlotNum slot number.
Definition: Inventory.cpp:472
void SendEquippedSlot()
Sends the equipped item slot to the client.
Definition: Inventory.cpp:380
void SetShieldSlot(const cItem &a_Item)
Sets current item in shield slot.
Definition: Inventory.cpp:362
const cItem & GetEquippedItem(void) const
Returns current equiped item.
Definition: Inventory.cpp:463
const cItem & GetShieldSlot() const
Returns current item in shield slot.
Definition: Inventory.cpp:454
void SetEquippedItem(const cItem &a_Item)
Sets current item in the equipped hotbar slot.
Definition: Inventory.cpp:371
Definition: Item.h:37
const cItemHandler & GetHandler(void) const
Returns the cItemHandler responsible for this item type.
Definition: Item.cpp:216
char m_ItemCount
Definition: Item.h:164
bool IsEmpty(void) const
Returns true if the item represents an empty stack - either the type is invalid, or count is zero.
Definition: Item.h:69
short m_ItemType
Definition: Item.h:163
short m_ItemDamage
Definition: Item.h:165
virtual void OnItemShoot(cPlayer *, const Vector3i a_BlockPos, eBlockFace a_BlockFace) const
Called when the client sends the SHOOT status in the lclk packet (releasing the bow).
Definition: ItemHandler.h:61
@ dlaBreakBlockInstant
Definition: ItemHandler.h:34
virtual bool OnDiggingBlock(cWorld *a_World, cPlayer *a_Player, const cItem &a_HeldItem, const Vector3i a_ClickedBlockPos, eBlockFace a_ClickedBlockFace) const
Called while the player digs a block using this item.
Encapsulates an in-game world map.
Definition: Map.h:83
RAII for cCriticalSection - locks the CS on creation, unlocks on destruction.
void Unlock(void)
void Authenticate(int a_ClientID, std::string_view a_Username, std::string_view a_ServerHash)
Queues a request for authenticating a user.
void BeginForgeHandshake(cClientHandle &a_Client)
Begin the Forge Modloader Handshake (FML|HS) sequence.
bool IsForgeClient
True if the client advertised itself as a Forge client.
void DataReceived(cClientHandle &a_Client, ContiguousByteBufferView a_Data)
Process received data from the client advancing the Forge handshake.
void HandleOutgoingData(ContiguousByteBuffer &a_Data)
Allows the protocol (if any) to do a final pass on outgiong data, possibly modifying the provided buf...
void HandleIncomingData(cClientHandle &a_Client, ContiguousByteBuffer &a_Data)
Directs incoming protocol data along the correct pathway, depending on the state of the version recog...
void SendDisconnect(cClientHandle &a_Client, const AString &a_Reason)
Sends a disconnect to the client as a result of a recognition error.
bool VersionRecognitionSuccessful()
Returns if we contain a concrete protocol corresponding to the client's protocol version.
cServer * GetServer(void)
Definition: Root.h:71
static cRoot * Get()
Definition: Root.h:52
void BroadcastChat(const AString &a_Message, eMessageType a_ChatPrefix=mtCustom)
Sends a chat message to all connected clients (in all worlds)
Definition: Root.cpp:657
AStringVector GetPlayerTabCompletionMultiWorld(const AString &a_Text)
Returns the completions for a player name across all worlds.
Definition: Root.cpp:983
void SendPlayerLists(cPlayer *a_DestPlayer)
Send playerlist of all worlds to player.
Definition: Root.cpp:609
cAuthenticator & GetAuthenticator(void)
Definition: Root.h:112
cWorld * GetDefaultWorld(void)
Definition: Root.cpp:455
cPluginManager * GetPluginManager(void)
Definition: Root.h:111
void BroadcastChatJoin(const AString &a_Message)
Definition: Root.h:178
cWorld * GetWorld(const AString &a_WorldName)
Returns a pointer to the world specified.
Definition: Root.cpp:465
void BroadcastPlayerListsAddPlayer(const cPlayer &a_Player, const cClientHandle *a_Exclude=nullptr)
Broadcast playerlist addition through all worlds.
Definition: Root.cpp:621
Definition: Server.h:56
void PlayerCreated()
Notifies the server that a player was created; the server uses this to adjust the number of players.
Definition: Server.cpp:135
size_t GetMaxPlayers(void) const
Definition: Server.h:70
const AString & GetProxySharedSecret(void) const
Definition: Server.h:159
const AString & GetDescription(void) const
Definition: Server.h:65
bool ShouldAllowBungeeCord(void) const
Returns true if BungeeCord logins (that specify the player's UUID) are allowed.
Definition: Server.h:155
void ClientMovedToWorld(const cClientHandle *a_Client)
Don't tick a_Client anymore, it will be ticked from its cPlayer instead.
Definition: Server.cpp:125
size_t GetNumPlayers(void) const
Definition: Server.h:71
Class that manages the statistics and achievements of a single player.
cBeaconEntity * GetBeaconEntity(void) const
Definition: BeaconWindow.h:27
short GetProperty(size_t a_Property)
Return the level requirement of the given enchantment slot.
cSlotAreaEnchanting * m_SlotArea
virtual const cItem * GetSlot(int a_SlotNum, cPlayer &a_Player) const override
Called to retrieve an item in the specified slot for the specified player.
Definition: SlotArea.cpp:2664
virtual void SetSlot(int a_SlotNum, cPlayer &a_Player, const cItem &a_Item) override
Called to set an item in the specified slot for the specified player.
Definition: SlotArea.cpp:1681
cItem SelectEnchantedOption(size_t a_EnchantOption)
Definition: SlotArea.cpp:1821
Represents a UI window.
Definition: Window.h:54
void SetSlot(cPlayer &a_Player, int a_SlotNum, const cItem &a_Item)
Sets the item to the specified slot for the specified player.
Definition: Window.cpp:121
@ wtWorkbench
Definition: Window.h:60
@ wtAnvil
Definition: Window.h:67
@ wtBeacon
Definition: Window.h:66
@ wtInventory
Definition: Window.h:58
@ wtEnchantment
Definition: Window.h:63
const cItem * GetSlot(cPlayer &a_Player, int a_SlotNum) const
Returns the item at the specified slot for the specified player.
Definition: Window.cpp:104
char GetWindowID(void) const
Definition: Window.h:80
virtual void Clicked(cPlayer &a_Player, int a_WindowID, short a_SlotNum, eClickAction a_ClickAction, const cItem &a_ClickedItem)
Handles a click event from a player.
Definition: Window.cpp:197
int GetWindowType(void) const
Definition: Window.h:81
Definition: UUID.h:11
static cUUID GenerateVersion3(const AString &a_Name)
Generates a version 3, variant 1 UUID based on the md5 hash of a_Name.
Definition: UUID.cpp:263
bool IsNil() const
Returns true if this contains the "nil" UUID with all bits set to 0.
Definition: UUID.h:30
UInt8 Version() const
Returns the version number of the UUID.
Definition: UUID.cpp:170
T x
Definition: Vector3.h:17
void Normalize(void)
Definition: Vector3.h:49
void Set(T a_x, T a_y, T a_z)
Definition: Vector3.h:42
T z
Definition: Vector3.h:17
bool Equals(const Vector3< T > &a_Rhs) const
Definition: Vector3.h:140
Definition: World.h:53
bool DoWithPlayerByUUID(const cUUID &a_PlayerUUID, cPlayerListCallback a_Callback)
Finds the player over his uuid and calls the callback.
Definition: World.cpp:2339
void TabCompleteUserName(const AString &a_Text, AStringVector &a_Results)
Appends all usernames starting with a_Text (case-insensitive) into Results.
Definition: World.cpp:2987
bool IsPVPEnabled(void) const
Definition: World.h:122
virtual void BroadcastEntityAnimation(const cEntity &a_Entity, EntityAnimation a_Animation, const cClientHandle *a_Exclude=nullptr) override
BLOCKTYPE GetBlock(Vector3i a_BlockPos) const
Returns the block type at the specified position.
Definition: World.h:363
bool DoWithEntityByID(UInt32 a_UniqueID, cEntityCallback a_Callback)
Calls the callback if the entity with the specified ID is found, with the entity object as the callba...
Definition: World.cpp:2464
void RemoveChunkClient(int a_ChunkX, int a_ChunkZ, cClientHandle *a_Client)
Removes client from the chunk specified.
Definition: World.cpp:2505
bool GetBlockTypeMeta(Vector3i a_BlockPos, BLOCKTYPE &a_BlockType, NIBBLETYPE &a_BlockMeta) const
Retrieves the block type and meta at the specified coords.
Definition: World.cpp:1779
int GetMaxViewDistance(void) const
Definition: World.h:711
virtual eDimension GetDimension(void) const override
Definition: World.h:133
virtual void BroadcastEntityEquipment(const cEntity &a_Entity, short a_SlotNum, const cItem &a_Item, const cClientHandle *a_Exclude=nullptr) override
bool SetSignLines(Vector3i a_BlockPos, const AString &a_Line1, const AString &a_Line2, const AString &a_Line3, const AString &a_Line4, cPlayer *a_Player=nullptr)
Sets the sign text, asking plugins for permission first.
Definition: World.cpp:2560
virtual void BroadcastBlockBreakAnimation(UInt32 a_EntityID, Vector3i a_BlockPos, Int8 a_Stage, const cClientHandle *a_Exclude=nullptr) override
cChunkMap * GetChunkMap(void)
Definition: World.h:852