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 "Server.h"
5 #include "World.h"
6 #include "Chunk.h"
7 #include "Entities/Pickup.h"
9 #include "Entities/Player.h"
10 #include "Entities/Minecart.h"
11 #include "Inventory.h"
16 #include "UI/Window.h"
17 #include "UI/AnvilWindow.h"
18 #include "UI/BeaconWindow.h"
19 #include "UI/EnchantingWindow.h"
20 #include "Item.h"
21 #include "Mobs/Monster.h"
22 #include "ChatColor.h"
23 #include "Items/ItemHandler.h"
24 #include "Blocks/BlockHandler.h"
25 #include "Blocks/BlockBed.h"
26 #include "Blocks/ChunkInterface.h"
28 
29 #include "Root.h"
30 
31 #include "Protocol/Authenticator.h"
33 #include "CompositeChat.h"
34 #include "Items/ItemSword.h"
35 
36 #include "mbedtls/md5.h"
37 
38 
39 
41 #define MAX_EXPLOSIONS_PER_TICK 20
42 
44 #define MAX_BLOCK_CHANGE_INTERACTIONS 20
45 
48 static const std::chrono::milliseconds PING_TIME_MS = std::chrono::milliseconds(1000);
49 
50 
51 
52 
53 
55 
56 
58 
59 
60 
62 // cClientHandle:
63 
64 cClientHandle::cClientHandle(const AString & a_IPString, int a_ViewDistance) :
65  m_LastSentDimension(dimNotSet),
66  m_ForgeHandshake(this),
67  m_CurrentViewDistance(a_ViewDistance),
68  m_RequestedViewDistance(a_ViewDistance),
69  m_IPString(a_IPString),
70  m_Player(nullptr),
71  m_CachedSentChunk(0, 0),
72  m_HasSentDC(false),
73  m_LastStreamedChunkX(0x7fffffff), // bogus chunk coords to force streaming upon login
74  m_LastStreamedChunkZ(0x7fffffff),
75  m_TicksSinceLastPacket(0),
76  m_Ping(1000),
77  m_PingID(1),
78  m_BlockDigAnimStage(-1),
79  m_BlockDigAnimSpeed(0),
80  m_BlockDigAnimX(0),
81  m_BlockDigAnimY(cChunkDef::Height + 1), // Invalid Y, so that the coords don't get picked up
82  m_BlockDigAnimZ(0),
83  m_HasStartedDigging(false),
84  m_LastDigBlockX(0),
85  m_LastDigBlockY(cChunkDef::Height + 1), // Invalid Y, so that the coords don't get picked up
86  m_LastDigBlockZ(0),
87  m_State(csConnected),
88  m_ShouldCheckDownloaded(false),
89  m_NumExplosionsThisTick(0),
90  m_NumBlockChangeInteractionsThisTick(0),
91  m_UniqueID(0),
92  m_HasSentPlayerChunk(false),
93  m_Locale("en_GB"),
94  m_LastPlacedSign(0, -1, 0),
95  m_ProtocolVersion(0)
96 {
97  m_Protocol = cpp14::make_unique<cProtocolRecognizer>(this);
98 
99  s_ClientCount++; // Not protected by CS because clients are always constructed from the same thread
101  m_PingStartTime = std::chrono::steady_clock::now();
102 
103  LOGD("New ClientHandle created at %p", static_cast<void *>(this));
104 }
105 
106 
107 
108 
109 
111 {
112  ASSERT(m_State == csDestroyed); // Has Destroy() been called?
113 
114  LOGD("Deleting client \"%s\" at %p", GetUsername().c_str(), static_cast<void *>(this));
115 
116  {
117  cCSLock Lock(m_CSChunkLists);
118  m_LoadedChunks.clear();
119  m_ChunksToSend.clear();
120  }
121 
122  if (m_Player != nullptr)
123  {
124  cWorld * World = m_Player->GetWorld();
125  if (World != nullptr)
126  {
130  }
131  // Send the Offline PlayerList packet:
133 
134  m_PlayerPtr.reset();
135  m_Player = nullptr;
136  }
137 
138  m_Protocol.reset();
139 
140  LOGD("ClientHandle at %p deleted", static_cast<void *>(this));
141 }
142 
143 
144 
145 
146 
148 {
149  {
151  m_Link.reset();
152  }
153 
154  // Temporary (#3115-will-fix): variable to keep track of whether the client authenticated and had the opportunity to have ownership transferred to the world
155  bool WasAddedToWorld = false;
156  {
157  cCSLock Lock(m_CSState);
158  WasAddedToWorld = (m_State >= csAuthenticated);
159  if (m_State >= csDestroying)
160  {
161  // Already called
162  LOGD("%s: client %p, \"%s\" already destroyed, bailing out", __FUNCTION__, static_cast<void *>(this), m_Username.c_str());
163  return;
164  }
166  }
167 
168  LOGD("%s: destroying client %p, \"%s\" @ %s", __FUNCTION__, static_cast<void *>(this), m_Username.c_str(), m_IPString.c_str());
169  auto player = m_Player;
170  m_Self.reset();
171  {
172  cCSLock lock(m_CSState);
173  m_State = csDestroyed; // Tick thread is allowed to call destructor async at any time after this
174  }
175 
176  if (player != nullptr)
177  {
178  // Atomically decrement player count (in world or server thread)
180 
181  auto world = player->GetWorld();
182  if (world != nullptr)
183  {
184  player->StopEveryoneFromTargetingMe();
185  player->SetIsTicking(false);
186 
187  if (WasAddedToWorld)
188  {
189  // If ownership was transferred, our own smart pointer should be unset
191 
192  m_PlayerPtr = world->RemovePlayer(*player, true);
193 
194  // And RemovePlayer should have returned a valid smart pointer
196  }
197  else
198  {
199  // If ownership was not transferred, our own smart pointer should be valid and RemovePlayer's should not
201  ASSERT(!world->IsPlayerReferencedInWorldOrChunk(*player));
202  }
203  }
204  player->RemoveClientHandle();
205  }
206 }
207 
208 
209 
210 
211 
213 {
215 }
216 
217 
218 
219 
220 
221 AString cClientHandle::FormatChatPrefix(bool ShouldAppendChatPrefixes, AString a_ChatPrefixS, AString m_Color1, AString m_Color2)
222 {
223  if (ShouldAppendChatPrefixes)
224  {
225  return Printf("%s[%s] %s", m_Color1.c_str(), a_ChatPrefixS.c_str(), m_Color2.c_str());
226  }
227  else
228  {
229  return Printf("%s", m_Color1.c_str());
230  }
231 }
232 
233 
234 
235 
236 
237 AString cClientHandle::FormatMessageType(bool ShouldAppendChatPrefixes, eMessageType a_ChatPrefix, const AString & a_AdditionalData)
238 {
239  switch (a_ChatPrefix)
240  {
241  case mtCustom: return "";
242  case mtFailure: return FormatChatPrefix(ShouldAppendChatPrefixes, "INFO", cChatColor::Rose, cChatColor::White);
243  case mtInformation: return FormatChatPrefix(ShouldAppendChatPrefixes, "INFO", cChatColor::Yellow, cChatColor::White);
244  case mtSuccess: return FormatChatPrefix(ShouldAppendChatPrefixes, "INFO", cChatColor::Green, cChatColor::White);
245  case mtWarning: return FormatChatPrefix(ShouldAppendChatPrefixes, "WARN", cChatColor::Rose, cChatColor::White);
246  case mtFatal: return FormatChatPrefix(ShouldAppendChatPrefixes, "FATAL", cChatColor::Red, cChatColor::White);
247  case mtDeath: return FormatChatPrefix(ShouldAppendChatPrefixes, "DEATH", cChatColor::Gray, cChatColor::White);
248  case mtJoin: return FormatChatPrefix(ShouldAppendChatPrefixes, "JOIN", cChatColor::Yellow, cChatColor::White);
249  case mtLeave: return FormatChatPrefix(ShouldAppendChatPrefixes, "LEAVE", cChatColor::Yellow, cChatColor::White);
250  case mtPrivateMessage:
251  {
252  if (ShouldAppendChatPrefixes)
253  {
254  return Printf("%s[MSG: %s] %s%s", cChatColor::LightBlue, a_AdditionalData.c_str(), cChatColor::White, cChatColor::Italic);
255  }
256  else
257  {
258  return Printf("%s: %s", a_AdditionalData.c_str(), cChatColor::LightBlue);
259  }
260  }
261  case mtMaxPlusOne: break;
262  }
263  return "";
264 }
265 
266 
267 
268 
269 
271 {
272  // Online UUIDs are always version 4 (random)
273  // We use Version 3 (MD5 hash) UUIDs for the offline UUIDs
274  // This guarantees that they will never collide with an online UUID and can be distinguished.
275  // This is also consistent with the vanilla offline UUID scheme.
276 
277  return cUUID::GenerateVersion3("OfflinePlayer:" + a_Username);
278 }
279 
280 
281 
282 
283 
284 bool cClientHandle::IsUUIDOnline(const cUUID & a_UUID)
285 {
286  // Online UUIDs are always version 4 (random)
287  // We use Version 3 (MD5 hash) UUIDs for the offline UUIDs
288  // This guarantees that they will never collide with an online UUID and can be distinguished.
289  return (a_UUID.Version() == 4);
290 }
291 
292 
293 
294 
295 
296 void cClientHandle::Kick(const AString & a_Reason)
297 {
298  if (m_State >= csAuthenticating) // Don't log pings
299  {
300  LOGINFO("Kicking player %s for \"%s\"", m_Username.c_str(), StripColorCodes(a_Reason).c_str());
301  }
302  SendDisconnect(a_Reason);
303 }
304 
305 
306 
307 
308 
309 void cClientHandle::Authenticate(const AString & a_Name, const cUUID & a_UUID, const Json::Value & a_Properties)
310 {
311  // Atomically increment player count (in server thread)
313 
314  {
315  cCSLock lock(m_CSState);
316  /*
317  LOGD("Processing authentication for client %s @ %s (%p), state = %d",
318  m_Username.c_str(), m_IPString.c_str(), static_cast<void *>(this), m_State.load()
319  );
320  //*/
321 
322  if (m_State != csAuthenticating)
323  {
324  return;
325  }
326 
327  ASSERT(m_Player == nullptr);
328 
329  m_Username = a_Name;
330 
331  // Only assign UUID and properties if not already pre-assigned (BungeeCord sends those in the Handshake packet):
332  if (m_UUID.IsNil())
333  {
334  m_UUID = a_UUID;
335  }
336  if (m_Properties.empty())
337  {
338  m_Properties = a_Properties;
339  }
340 
341  // Send login success (if the protocol supports it):
342  m_Protocol->SendLoginSuccess();
343 
345  {
346  m_ForgeHandshake.BeginForgeHandshake(a_Name, a_UUID, a_Properties);
347  }
348  else
349  {
350  FinishAuthenticate(a_Name, a_UUID, a_Properties);
351  }
352  }
353 }
354 
355 
356 
357 
358 
359 void cClientHandle::FinishAuthenticate(const AString & a_Name, const cUUID & a_UUID, const Json::Value & a_Properties)
360 {
361  cWorld * World;
362  {
363  // Spawn player (only serversided, so data is loaded)
364  m_PlayerPtr = cpp14::make_unique<cPlayer>(m_Self, GetUsername());
365  m_Player = m_PlayerPtr.get();
366  /*
367  LOGD("Created a new cPlayer object at %p for client %s @ %s (%p)",
368  static_cast<void *>(m_Player),
369  m_Username.c_str(), m_IPString.c_str(), static_cast<void *>(this)
370  );
371  //*/
373  m_Self.reset();
374 
375 
376  // New player use default world
377  // Player who can load from disk, use loaded world
378  if (m_Player->GetWorld() == nullptr)
379  {
380  World = cRoot::Get()->GetWorld(m_Player->GetLoadedWorldName());
381  if (World == nullptr)
382  {
383  World = cRoot::Get()->GetDefaultWorld();
384  m_Player->SetPosition(World->GetSpawnX(), World->GetSpawnY(), World->GetSpawnZ());
385  }
386  m_Player->SetWorld(World);
387  }
388  else
389  {
390  World = m_Player->GetWorld();
391  }
392 
393  m_Player->SetIP (m_IPString);
394 
395  if (!cRoot::Get()->GetPluginManager()->CallHookPlayerJoined(*m_Player))
396  {
397  cRoot::Get()->BroadcastChatJoin(Printf("%s has joined the game", GetUsername().c_str()));
398  LOGINFO("Player %s has joined the game", m_Username.c_str());
399  }
400 
401  m_ConfirmPosition = m_Player->GetPosition();
402 
403  // Return a server login packet
404  m_Protocol->SendLogin(*m_Player, *World);
406 
407  // Send Weather if raining:
408  if ((World->GetWeather() == 1) || (World->GetWeather() == 2))
409  {
410  m_Protocol->SendWeather(World->GetWeather());
411  }
412 
413  // Send time:
414  m_Protocol->SendTimeUpdate(World->GetWorldAge(), World->GetTimeOfDay(), World->IsDaylightCycleEnabled());
415 
416  // Send contents of the inventory window
417  m_Protocol->SendWholeInventory(*m_Player->GetWindow());
418 
419  // Send health
420  m_Player->SendHealth();
421 
422  // Send experience
423  m_Player->SendExperience();
424 
425  // Send hotbar active slot
426  m_Player->SendHotbarActiveSlot();
427 
428  // Send player list items
429  SendPlayerListAddPlayer(*m_Player);
431  cRoot::Get()->SendPlayerLists(m_Player);
432 
434  }
435 
436  // Query player team
437  m_Player->UpdateTeam();
438 
439  // Send scoreboard data
440  World->GetScoreBoard().SendTo(*this);
441 
442  // Send statistics
444 
445  // Delay the first ping until the client "settles down"
446  // This should fix #889, "BadCast exception, cannot convert bit to fm" error in client
447  m_PingStartTime = std::chrono::steady_clock::now() + std::chrono::seconds(3); // Send the first KeepAlive packet in 3 seconds
448 
450  // LOGD("Client %s @ %s (%p) has been fully authenticated", m_Username.c_str(), m_IPString.c_str(), static_cast<void *>(this));
451 }
452 
453 
454 
455 
456 
458 {
460  {
461  return true;
462  }
463  ASSERT(m_Player != nullptr);
464 
465  int ChunkPosX = m_Player->GetChunkX();
466  int ChunkPosZ = m_Player->GetChunkZ();
467  if ((m_LastStreamedChunkX == ChunkPosX) && (m_LastStreamedChunkZ == ChunkPosZ))
468  {
469  // All chunks are already loaded. Abort loading.
470  return true;
471  }
472 
473  // Get the look vector and normalize it.
474  Vector3d Position = m_Player->GetEyePosition();
475  Vector3d LookVector = m_Player->GetLookVector();
476  LookVector.Normalize();
477 
478  // Lock the list
479  cCSLock Lock(m_CSChunkLists);
480 
481  // High priority: Load the chunks that are in the view-direction of the player (with a radius of 3)
482  for (int Range = 0; Range < m_CurrentViewDistance; Range++)
483  {
484  Vector3d Vector = Position + LookVector * cChunkDef::Width * Range;
485 
486  // Get the chunk from the x / z coords.
487  int RangeX, RangeZ = 0;
488  cChunkDef::BlockToChunk(FloorC(Vector.x), FloorC(Vector.z), RangeX, RangeZ);
489 
490  for (int X = 0; X < 7; X++)
491  {
492  for (int Z = 0; Z < 7; Z++)
493  {
494  int ChunkX = RangeX + ((X >= 4) ? (3 - X) : X);
495  int ChunkZ = RangeZ + ((Z >= 4) ? (3 - Z) : Z);
496  cChunkCoords Coords(ChunkX, ChunkZ);
497 
498  // Checks if the chunk is in distance
499  if ((Diff(ChunkX, ChunkPosX) > m_CurrentViewDistance) || (Diff(ChunkZ, ChunkPosZ) > m_CurrentViewDistance))
500  {
501  continue;
502  }
503 
504  // If the chunk already loading / loaded -> skip
505  if (
506  (m_ChunksToSend.find(Coords) != m_ChunksToSend.end()) ||
507  (m_LoadedChunks.find(Coords) != m_LoadedChunks.end())
508  )
509  {
510  continue;
511  }
512 
513  // Unloaded chunk found -> Send it to the client.
514  Lock.Unlock();
516  return false;
517  }
518  }
519  }
520 
521  // Low priority: Add all chunks that are in range. (From the center out to the edge)
522  for (int d = 0; d <= m_CurrentViewDistance; ++d) // cycle through (square) distance, from nearest to furthest
523  {
524  // For each distance add chunks in a hollow square centered around current position:
525  cChunkCoordsList CurcleChunks;
526  for (int i = -d; i <= d; ++i)
527  {
528  CurcleChunks.push_back(cChunkCoords(ChunkPosX + d, ChunkPosZ + i));
529  CurcleChunks.push_back(cChunkCoords(ChunkPosX - d, ChunkPosZ + i));
530  }
531  for (int i = -d + 1; i < d; ++i)
532  {
533  CurcleChunks.push_back(cChunkCoords(ChunkPosX + i, ChunkPosZ + d));
534  CurcleChunks.push_back(cChunkCoords(ChunkPosX + i, ChunkPosZ - d));
535  }
536 
537  // For each the CurcleChunks list and send the first unloaded chunk:
538  for (cChunkCoordsList::iterator itr = CurcleChunks.begin(), end = CurcleChunks.end(); itr != end; ++itr)
539  {
540  cChunkCoords Coords = *itr;
541 
542  // If the chunk already loading / loaded -> skip
543  if (
544  (m_ChunksToSend.find(Coords) != m_ChunksToSend.end()) ||
545  (m_LoadedChunks.find(Coords) != m_LoadedChunks.end())
546  )
547  {
548  continue;
549  }
550 
551  // Unloaded chunk found -> Send it to the client.
552  Lock.Unlock();
554  return false;
555  }
556  }
557 
558  // All chunks are loaded -> Sets the last loaded chunk coordinates to current coordinates
559  m_LastStreamedChunkX = ChunkPosX;
560  m_LastStreamedChunkZ = ChunkPosZ;
561  return true;
562 }
563 
564 
565 
566 
567 
569 {
570  int ChunkPosX = FAST_FLOOR_DIV(static_cast<int>(m_Player->GetPosX()), cChunkDef::Width);
571  int ChunkPosZ = FAST_FLOOR_DIV(static_cast<int>(m_Player->GetPosZ()), cChunkDef::Width);
572 
573  cChunkCoordsList ChunksToRemove;
574  {
575  cCSLock Lock(m_CSChunkLists);
576  for (auto itr = m_LoadedChunks.begin(); itr != m_LoadedChunks.end();)
577  {
578  int DiffX = Diff((*itr).m_ChunkX, ChunkPosX);
579  int DiffZ = Diff((*itr).m_ChunkZ, ChunkPosZ);
580  if ((DiffX > m_CurrentViewDistance) || (DiffZ > m_CurrentViewDistance))
581  {
582  ChunksToRemove.push_back(*itr);
583  itr = m_LoadedChunks.erase(itr);
584  }
585  else
586  {
587  ++itr;
588  }
589  }
590 
591  for (auto itr = m_ChunksToSend.begin(); itr != m_ChunksToSend.end();)
592  {
593  int DiffX = Diff((*itr).m_ChunkX, ChunkPosX);
594  int DiffZ = Diff((*itr).m_ChunkZ, ChunkPosZ);
595  if ((DiffX > m_CurrentViewDistance) || (DiffZ > m_CurrentViewDistance))
596  {
597  itr = m_ChunksToSend.erase(itr);
598  }
599  else
600  {
601  ++itr;
602  }
603  }
604  }
605 
606  for (cChunkCoordsList::iterator itr = ChunksToRemove.begin(); itr != ChunksToRemove.end(); ++itr)
607  {
608  m_Player->GetWorld()->RemoveChunkClient(itr->m_ChunkX, itr->m_ChunkZ, this);
609  SendUnloadChunk(itr->m_ChunkX, itr->m_ChunkZ);
610  }
611 }
612 
613 
614 
615 
616 
617 void cClientHandle::StreamChunk(int a_ChunkX, int a_ChunkZ, cChunkSender::eChunkPriority a_Priority)
618 {
619  if (m_State >= csDestroying)
620  {
621  // Don't stream chunks to clients that are being destroyed
622  return;
623  }
624 
625  cWorld * World = m_Player->GetWorld();
626  ASSERT(World != nullptr);
627 
628  if (World->AddChunkClient(a_ChunkX, a_ChunkZ, this))
629  {
630  {
631  cCSLock Lock(m_CSChunkLists);
632  m_LoadedChunks.emplace(a_ChunkX, a_ChunkZ);
633  m_ChunksToSend.emplace(a_ChunkX, a_ChunkZ);
634  }
635  World->SendChunkTo(a_ChunkX, a_ChunkZ, a_Priority, this);
636  }
637 }
638 
639 
640 
641 
642 
644 {
645  cWorld * World = m_Player->GetWorld();
646  if (World != nullptr)
647  {
648  World->RemoveClientFromChunks(this);
649  }
650 
651  {
652  // Reset all chunk lists:
653  cCSLock Lock(m_CSChunkLists);
654  m_LoadedChunks.clear();
655  m_ChunksToSend.clear();
656  m_SentChunks.clear();
657 
658  // Also reset the LastStreamedChunk coords to bogus coords,
659  // so that all chunks are streamed in subsequent StreamChunks() call (FS #407)
660  m_LastStreamedChunkX = 0x7fffffff;
661  m_LastStreamedChunkZ = 0x7fffffff;
662  }
663 }
664 
665 
666 
667 
668 
669 void cClientHandle::HandleNPCTrade(int a_SlotNum)
670 {
671  // TODO
672  LOGWARNING("%s: Not implemented yet", __FUNCTION__);
673 }
674 
675 
676 
677 
678 
680 {
681  if (m_Player->GetUniqueID() == a_EntityID)
682  {
684  }
685 }
686 
687 
688 
689 
690 
692 {
693  /* TODO: unused function, handles Legacy Server List Ping
694  http://wiki.vg/Protocol#Legacy_Server_List_Ping suggests that servers SHOULD handle this packet */
695 
696  // Somebody tries to retrieve information about the server
697  AString Reply;
698  const cServer & Server = *cRoot::Get()->GetServer();
699 
700  Printf(Reply, "%s%s%zu%s%zu",
701  Server.GetDescription().c_str(),
703  Server.GetNumPlayers(),
705  Server.GetMaxPlayers()
706  );
707  Kick(Reply);
708 }
709 
710 
711 
712 
713 
714 bool cClientHandle::HandleLogin(const AString & a_Username)
715 {
716  {
717  cCSLock lock(m_CSState);
718  if (m_State != csConnected)
719  {
720  /*
721  LOGD("Client %s @ %s (%p, state %d) has disconnected before logging in, bailing out of login",
722  a_Username.c_str(), m_IPString.c_str(), static_cast<void *>(this), m_State.load()
723  );
724  //*/
725  return false;
726  }
727 
728  // 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());
729 
730  m_Username = a_Username;
731 
732  // Let the plugins know about this event, they may refuse the player:
733  if (cRoot::Get()->GetPluginManager()->CallHookLogin(*this, m_ProtocolVersion, a_Username))
734  {
735  SendDisconnect("Login Rejected!");
736  return false;
737  }
738 
740  } // lock(m_CSState)
741 
742  // Schedule for authentication; until then, let the player wait (but do not block)
744  return true;
745 }
746 
747 
748 
749 
750 
751 void cClientHandle::HandleCreativeInventory(Int16 a_SlotNum, const cItem & a_HeldItem, eClickAction a_ClickAction)
752 {
753  // This is for creative Inventory changes
755  {
756  LOGWARNING("Got a CreativeInventoryAction packet from user \"%s\" while not in creative mode. Ignoring.", m_Username.c_str());
757  return;
758  }
760  {
761  LOGWARNING("Got a CreativeInventoryAction packet from user \"%s\" while not in the inventory window. Ignoring.", m_Username.c_str());
762  return;
763  }
764 
765  m_Player->GetWindow()->Clicked(*m_Player, 0, a_SlotNum, a_ClickAction, a_HeldItem);
766 }
767 
768 
769 
770 
771 
772 void cClientHandle::HandleEnchantItem(UInt8 a_WindowID, UInt8 a_Enchantment)
773 {
774  if (a_Enchantment > 2)
775  {
776  LOGWARNING("%s attempt to crash the server with invalid enchanting selection (%u)!", GetUsername().c_str(), a_Enchantment);
777  Kick("Invalid enchanting!");
778  return;
779  }
780 
781  if (
782  (m_Player->GetWindow() == nullptr) ||
783  (m_Player->GetWindow()->GetWindowID() != a_WindowID) ||
785  )
786  {
787  return;
788  }
789 
790  cEnchantingWindow * Window = static_cast<cEnchantingWindow *>(m_Player->GetWindow());
791  cItem Item = *Window->m_SlotArea->GetSlot(0, *m_Player); // Make a copy of the item
792  short BaseEnchantmentLevel = Window->GetPropertyValue(a_Enchantment);
793 
794  if (Item.EnchantByXPLevels(BaseEnchantmentLevel))
795  {
796  if (m_Player->IsGameModeCreative() || m_Player->DeltaExperience(-m_Player->XpForLevel(BaseEnchantmentLevel)) >= 0)
797  {
798  Window->m_SlotArea->SetSlot(0, *m_Player, Item);
799  Window->SendSlot(*m_Player, Window->m_SlotArea, 0);
800  Window->BroadcastWholeWindow();
801 
802  Window->SetProperty(0, 0, *m_Player);
803  Window->SetProperty(1, 0, *m_Player);
804  Window->SetProperty(2, 0, *m_Player);
805  }
806  }
807 }
808 
809 
810 
811 
812 
813 void cClientHandle::HandlePlayerAbilities(bool a_CanFly, bool a_IsFlying, float FlyingSpeed, float WalkingSpeed)
814 {
815  UNUSED(FlyingSpeed); // Ignore the client values for these
816  UNUSED(WalkingSpeed);
817 
818  m_Player->SetCanFly(a_CanFly);
819  m_Player->SetFlying(a_IsFlying);
820 }
821 
822 
823 
824 
825 
826 void cClientHandle::HandlePlayerPos(double a_PosX, double a_PosY, double a_PosZ, double a_Stance, bool a_IsOnGround)
827 {
828  if ((m_Player == nullptr) || (m_State != csPlaying))
829  {
830  // The client hasn't been spawned yet and sends nonsense, we know better
831  return;
832  }
833 
834  if (m_Player->IsFrozen())
835  {
836  // Ignore client-side updates if the player is frozen
837  return;
838  }
839 
840  Vector3d NewPosition(a_PosX, a_PosY, a_PosZ);
841  Vector3d OldPosition = GetPlayer()->GetPosition();
842  auto PreviousIsOnGround = GetPlayer()->IsOnGround();
843 
844  // If the player has moved too far, "repair" them:
845  if ((OldPosition - NewPosition).SqrLength() > 100 * 100)
846  {
847  LOGD("Too far away (%0.2f), \"repairing\" the client", (OldPosition - NewPosition).Length());
849  return;
850  }
851 
852  if (cRoot::Get()->GetPluginManager()->CallHookPlayerMoving(*m_Player, OldPosition, NewPosition))
853  {
855  return;
856  }
857 
858  // TODO: should do some checks to see if player is not moving through terrain
859  // TODO: Official server refuses position packets too far away from each other, kicking "hacked" clients; we should, too
860 
861  m_Player->SetPosition(NewPosition);
862  m_Player->SetStance(a_Stance);
863  m_Player->SetTouchGround(a_IsOnGround);
864  m_Player->UpdateMovementStats(NewPosition - OldPosition, PreviousIsOnGround);
865 }
866 
867 
868 
869 
870 
871 void cClientHandle::HandlePluginMessage(const AString & a_Channel, const AString & a_Message)
872 {
873  if (a_Channel == "REGISTER")
874  {
875  if (HasPluginChannel(a_Channel))
876  {
877  SendPluginMessage("UNREGISTER", a_Channel);
878  return; // Can't register again if already taken - kinda defeats the point of plugin messaging!
879  }
880 
882  }
883  else if (a_Channel == "UNREGISTER")
884  {
886  }
887  else if (a_Channel == "FML|HS")
888  {
889  m_ForgeHandshake.DataReceived(this, a_Message.c_str(), a_Message.size());
890  }
891  else if (!HasPluginChannel(a_Channel))
892  {
893  // Ignore if client sent something but didn't register the channel first
894  LOGD("Player %s sent a plugin message on channel \"%s\", but didn't REGISTER it first", GetUsername().c_str(), a_Channel.c_str());
895  SendPluginMessage("UNREGISTER", a_Channel);
896  return;
897  }
898 
899  cPluginManager::Get()->CallHookPluginMessage(*this, a_Channel, a_Message);
900 }
901 
902 
903 
904 
905 
907 {
908  // Break the string on each NUL character.
909  // Note that StringSplit() doesn't work on this because NUL is a special char - string terminator
910  size_t len = a_PluginChannels.size();
911  size_t first = 0;
912  AStringVector res;
913  for (size_t i = 0; i < len; i++)
914  {
915  if (a_PluginChannels[i] != 0)
916  {
917  continue;
918  }
919  if (i > first)
920  {
921  res.push_back(a_PluginChannels.substr(first, i - first));
922  }
923  first = i + 1;
924  } // for i - a_PluginChannels[]
925  if (first < len)
926  {
927  res.push_back(a_PluginChannels.substr(first, len - first));
928  }
929  return res;
930 }
931 
932 
933 
934 
935 
937 {
938  for (AStringVector::const_iterator itr = a_ChannelList.begin(), end = a_ChannelList.end(); itr != end; ++itr)
939  {
940  m_PluginChannels.insert(*itr);
941  } // for itr - a_ChannelList[]
942 }
943 
944 
945 
946 
947 
949 {
950  for (AStringVector::const_iterator itr = a_ChannelList.begin(), end = a_ChannelList.end(); itr != end; ++itr)
951  {
952  m_PluginChannels.erase(*itr);
953  } // for itr - a_ChannelList[]
954 }
955 
956 
957 
958 
959 
960 void cClientHandle::HandleBeaconSelection(int a_PrimaryEffect, int a_SecondaryEffect)
961 {
962  cWindow * Window = m_Player->GetWindow();
963  if ((Window == nullptr) || (Window->GetWindowType() != cWindow::wtBeacon))
964  {
965  return;
966  }
967  cBeaconWindow * BeaconWindow = static_cast<cBeaconWindow *>(Window);
968 
969  if (Window->GetSlot(*m_Player, 0)->IsEmpty())
970  {
971  return;
972  }
973 
975  if ((a_PrimaryEffect >= 0) && (a_PrimaryEffect <= static_cast<int>(cEntityEffect::effSaturation)))
976  {
977  PrimaryEffect = static_cast<cEntityEffect::eType>(a_PrimaryEffect);
978  }
980  if ((a_SecondaryEffect >= 0) && (a_SecondaryEffect <= static_cast<int>(cEntityEffect::effSaturation)))
981  {
982  SecondaryEffect = static_cast<cEntityEffect::eType>(a_SecondaryEffect);
983  }
984 
985  Window->SetSlot(*m_Player, 0, cItem());
986  BeaconWindow->GetBeaconEntity()->SetPrimaryEffect(PrimaryEffect);
987 
988  // Valid effect check. Vanilla don't check this, but we do it :)
989  if (
990  (SecondaryEffect == cEntityEffect::effNoEffect) ||
991  (SecondaryEffect == cEntityEffect::effRegeneration) ||
992  (SecondaryEffect == BeaconWindow->GetBeaconEntity()->GetPrimaryEffect())
993  )
994  {
995  BeaconWindow->GetBeaconEntity()->SetSecondaryEffect(SecondaryEffect);
996  }
997  else
998  {
1000  }
1001 
1002  m_Player->CloseWindow(true);
1003 }
1004 
1005 
1006 
1007 
1008 
1009 void cClientHandle::HandleCommandBlockBlockChange(int a_BlockX, int a_BlockY, int a_BlockZ, const AString & a_NewCommand)
1010 {
1011  if (a_NewCommand.empty())
1012  {
1013  Kick("Command block string unexpectedly empty - hacked client?");
1014  return;
1015  }
1016 
1017  cWorld * World = m_Player->GetWorld();
1018  if (World->AreCommandBlocksEnabled())
1019  {
1020  World->SetCommandBlockCommand(a_BlockX, a_BlockY, a_BlockZ, a_NewCommand);
1021  SendChat("Successfully set command block command", mtSuccess);
1022  }
1023  else
1024  {
1025  SendChat("Command blocks are not enabled on this server", mtFailure);
1026  }
1027 }
1028 
1029 
1030 
1031 
1032 
1033 void cClientHandle::HandleCommandBlockEntityChange(UInt32 a_EntityID, const AString & a_NewCommand)
1034 {
1035  // TODO
1036  LOGWARNING("%s: Not implemented yet", __FUNCTION__);
1037 }
1038 
1039 
1040 
1041 
1042 
1044 {
1045  if ((m_Player->GetWindow() == nullptr) || (m_Player->GetWindow()->GetWindowType() != cWindow::wtAnvil))
1046  {
1047  return;
1048  }
1049 
1050  if (a_ItemName.length() <= 30)
1051  {
1052  static_cast<cAnvilWindow *>(m_Player->GetWindow())->SetRepairedItemName(a_ItemName, m_Player);
1053  }
1054 }
1055 
1056 
1057 
1058 
1059 
1060 void cClientHandle::HandleLeftClick(int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, UInt8 a_Status)
1061 {
1062  FLOGD("HandleLeftClick: {0}; Face: {1}; Stat: {2}",
1063  Vector3i{a_BlockX, a_BlockY, a_BlockZ}, a_BlockFace, a_Status
1064  );
1065 
1067 
1069  {
1070  Kick("Too many blocks were destroyed per unit time - hacked client?");
1071  return;
1072  }
1073 
1074  if ((a_Status == DIG_STATUS_STARTED) || (a_Status == DIG_STATUS_FINISHED))
1075  {
1076  if (a_BlockFace == BLOCK_FACE_NONE)
1077  {
1078  return;
1079  }
1080 
1081  /* Check for clickthrough-blocks:
1082  When the user breaks a fire block, the client send the wrong block location.
1083  We must find the right block with the face direction. */
1084  int BlockX = a_BlockX;
1085  int BlockY = a_BlockY;
1086  int BlockZ = a_BlockZ;
1087  AddFaceDirection(BlockX, BlockY, BlockZ, a_BlockFace);
1088 
1089  if (
1090  cChunkDef::IsValidHeight(BlockY) &&
1091  cBlockInfo::GetHandler(m_Player->GetWorld()->GetBlock(BlockX, BlockY, BlockZ))->IsClickedThrough()
1092  )
1093  {
1094  a_BlockX = BlockX;
1095  a_BlockY = BlockY;
1096  a_BlockZ = BlockZ;
1097  }
1098 
1099  if (
1100  ((Diff(m_Player->GetPosX(), static_cast<double>(a_BlockX)) > 6) ||
1101  (Diff(m_Player->GetPosY(), static_cast<double>(a_BlockY)) > 6) ||
1102  (Diff(m_Player->GetPosZ(), static_cast<double>(a_BlockZ)) > 6))
1103  )
1104  {
1105  m_Player->GetWorld()->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, *m_Player);
1106  return;
1107  }
1108  }
1109 
1110  cPluginManager * PlgMgr = cRoot::Get()->GetPluginManager();
1111  if (m_Player->IsFrozen() || PlgMgr->CallHookPlayerLeftClick(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, static_cast<char>(a_Status)))
1112  {
1113  // A plugin doesn't agree with the action, replace the block on the client and quit:
1114  m_Player->GetWorld()->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, *m_Player);
1115  SendPlayerPosition(); // Prevents the player from falling through the block that was temporarily broken client side.
1116  return;
1117  }
1118 
1119  switch (a_Status)
1120  {
1121  case DIG_STATUS_DROP_HELD: // Drop held item
1122  {
1123  if (PlgMgr->CallHookPlayerTossingItem(*m_Player))
1124  {
1125  // A plugin doesn't agree with the tossing. The plugin itself is responsible for handling the consequences (possible inventory mismatch)
1126  return;
1127  }
1128 
1130  return;
1131  }
1132 
1133  case DIG_STATUS_SHOOT_EAT:
1134  {
1136  if (ItemHandler->IsFood() || ItemHandler->IsDrinkable(m_Player->GetEquippedItem().m_ItemDamage))
1137  {
1138  m_Player->AbortEating();
1139  return;
1140  }
1141  else
1142  {
1143  if (PlgMgr->CallHookPlayerShooting(*m_Player))
1144  {
1145  // A plugin doesn't agree with the action. The plugin itself is responsible for handling the consequences (possible inventory mismatch)
1146  return;
1147  }
1148  // When bow is in off-hand / shield slot
1150  {
1152  }
1153  ItemHandler->OnItemShoot(m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace);
1154  }
1155  return;
1156  }
1157 
1158  case DIG_STATUS_STARTED:
1159  {
1160  BLOCKTYPE OldBlock;
1161  NIBBLETYPE OldMeta;
1162  m_Player->GetWorld()->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, OldBlock, OldMeta);
1163  HandleBlockDigStarted(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, OldBlock, OldMeta);
1164  return;
1165  }
1166 
1167  case DIG_STATUS_FINISHED:
1168  {
1169  BLOCKTYPE OldBlock;
1170  NIBBLETYPE OldMeta;
1171  m_Player->GetWorld()->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, OldBlock, OldMeta);
1172  HandleBlockDigFinished(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, OldBlock, OldMeta);
1173  return;
1174  }
1175 
1176  case DIG_STATUS_CANCELLED:
1177  {
1178  // Block breaking cancelled by player
1180  return;
1181  }
1182 
1183  case DIG_STATUS_DROP_STACK:
1184  {
1185  if (PlgMgr->CallHookPlayerTossingItem(*m_Player))
1186  {
1187  // A plugin doesn't agree with the tossing. The plugin itself is responsible for handling the consequences (possible inventory mismatch)
1188  return;
1189  }
1190  m_Player->TossEquippedItem(64); // Toss entire slot - if there aren't enough items, the maximum will be ejected
1191  return;
1192  }
1193 
1195  {
1196 
1197  cItem EquippedItem = m_Player->GetEquippedItem();
1198  cItem OffhandItem = m_Player->GetOffHandEquipedItem();
1199 
1200  cInventory & Intentory = m_Player->GetInventory();
1201  Intentory.SetShieldSlot(EquippedItem);
1202  Intentory.SetHotbarSlot(Intentory.GetEquippedSlotNum(), OffhandItem);
1203 
1204  return;
1205  }
1206 
1207  default:
1208  {
1209  ASSERT(!"Unhandled DIG_STATUS");
1210  return;
1211  }
1212  } // switch (a_Status)
1213 }
1214 
1215 
1216 
1217 
1218 
1219 void cClientHandle::HandleBlockDigStarted(int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, BLOCKTYPE a_OldBlock, NIBBLETYPE a_OldMeta)
1220 {
1221  if (
1223  (a_BlockX == m_LastDigBlockX) &&
1224  (a_BlockY == m_LastDigBlockY) &&
1225  (a_BlockZ == m_LastDigBlockZ)
1226  )
1227  {
1228  // It is a duplicate packet, drop it right away
1229  return;
1230  }
1231 
1232  if (
1235  (m_Player->GetWorld()->GetBlock(a_BlockX, a_BlockY, a_BlockZ) != E_BLOCK_FIRE)
1236  )
1237  {
1238  // Players can't destroy blocks with a sword in the hand.
1239  return;
1240  }
1241 
1242  if (
1243  (Diff(m_Player->GetPosX(), static_cast<double>(a_BlockX)) > 6) ||
1244  (Diff(m_Player->GetPosY(), static_cast<double>(a_BlockY)) > 6) ||
1245  (Diff(m_Player->GetPosZ(), static_cast<double>(a_BlockZ)) > 6)
1246  )
1247  {
1248  m_Player->GetWorld()->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, *m_Player);
1249  return;
1250  }
1251 
1252  // 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:
1253  m_HasStartedDigging = true;
1254  m_LastDigBlockX = a_BlockX;
1255  m_LastDigBlockY = a_BlockY;
1256  m_LastDigBlockZ = a_BlockZ;
1257 
1258  if (
1259  (m_Player->IsGameModeCreative()) || // In creative mode, digging is done immediately
1260  cBlockInfo::IsOneHitDig(a_OldBlock) // One-hit blocks get destroyed immediately, too
1261  )
1262  {
1263  HandleBlockDigFinished(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_OldBlock, a_OldMeta);
1264  return;
1265  }
1266 
1267  m_BreakProgress = 0;
1268 
1269  // Start dig animation
1270  // TODO: calculate real animation speed
1271  // TODO: Send animation packets even without receiving any other packets
1272  m_BlockDigAnimSpeed = 10;
1273  m_BlockDigAnimX = a_BlockX;
1274  m_BlockDigAnimY = a_BlockY;
1275  m_BlockDigAnimZ = a_BlockZ;
1276  m_BlockDigAnimStage = 0;
1278 
1279  cWorld * World = m_Player->GetWorld();
1280  cChunkInterface ChunkInterface(World->GetChunkMap());
1281  cBlockHandler * Handler = cBlockInfo::GetHandler(a_OldBlock);
1282  Handler->OnDigging(ChunkInterface, *World, *m_Player, a_BlockX, a_BlockY, a_BlockZ);
1283 
1285  ItemHandler->OnDiggingBlock(World, m_Player, m_Player->GetEquippedItem(), a_BlockX, a_BlockY, a_BlockZ, a_BlockFace);
1286 }
1287 
1288 
1289 
1290 
1291 
1292 void cClientHandle::HandleBlockDigFinished(int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, BLOCKTYPE a_OldBlock, NIBBLETYPE a_OldMeta)
1293 {
1294  if (
1295  !m_HasStartedDigging || // Hasn't received the DIG_STARTED packet
1296  (m_LastDigBlockX != a_BlockX) || // DIG_STARTED has had different pos
1297  (m_LastDigBlockY != a_BlockY) ||
1298  (m_LastDigBlockZ != a_BlockZ)
1299  )
1300  {
1301  FLOGD("Prevented a dig / aim bug in the client (finish {0} vs start {1}, HSD: {2})",
1302  Vector3i{a_BlockX, a_BlockY, a_BlockZ},
1304  (m_HasStartedDigging ? "True" : "False")
1305  );
1306  return;
1307  }
1308 
1310 
1311  if (!m_Player->IsGameModeCreative())
1312  {
1313  if (a_OldBlock == E_BLOCK_BEDROCK)
1314  {
1315  Kick("You can't break a bedrock!");
1316  return;
1317  }
1318  if (a_OldBlock == E_BLOCK_BARRIER)
1319  {
1320  Kick("You can't break a barrier!");
1321  return;
1322  }
1323  }
1324 
1325  if (!m_Player->IsGameModeCreative() && !cBlockInfo::IsOneHitDig(a_OldBlock))
1326  {
1327  // Fix for very fast tools.
1330  {
1331  LOGD("Break progress of player %s was less than expected: %f < %f\n", m_Player->GetName().c_str(), m_BreakProgress * 100, FASTBREAK_PERCENTAGE * 100);
1332  // AntiFastBreak doesn't agree with the breaking. Bail out. Send the block back to the client, so that it knows:
1333  m_Player->GetWorld()->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, *m_Player);
1334  m_Player->GetWorld()->SendBlockTo(a_BlockX, a_BlockY + 1, a_BlockZ, *m_Player); // Strange bug with doors
1335  SendPlayerPosition(); // Prevents the player from falling through the block that was temporarily broken client side.
1336  m_Player->SendMessage("FastBreak?"); // TODO Anticheat hook
1337  return;
1338  }
1339  }
1340 
1341  cWorld * World = m_Player->GetWorld();
1342 
1343  if (cRoot::Get()->GetPluginManager()->CallHookPlayerBreakingBlock(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_OldBlock, a_OldMeta))
1344  {
1345  // A plugin doesn't agree with the breaking. Bail out. Send the block back to the client, so that it knows:
1346  m_Player->GetWorld()->SendBlockTo(a_BlockX, a_BlockY, a_BlockZ, *m_Player);
1347  m_Player->GetWorld()->SendBlockTo(a_BlockX, a_BlockY + 1, a_BlockZ, *m_Player); // Strange bug with doors
1348  SendPlayerPosition(); // Prevents the player from falling through the block that was temporarily broken client side.
1349  return;
1350  }
1351 
1352  if (a_OldBlock == E_BLOCK_AIR)
1353  {
1354  return;
1355  }
1356 
1357  m_Player->AddFoodExhaustion(0.025);
1358  cChunkInterface ChunkInterface(World->GetChunkMap());
1359  auto blockHandler = BlockHandler(a_OldBlock);
1360  Vector3i absPos(a_BlockX, a_BlockY, a_BlockZ);
1361  blockHandler->OnPlayerBreakingBlock(ChunkInterface, *World, *m_Player, absPos);
1363  {
1364  World->DropBlockAsPickups(absPos, m_Player, &m_Player->GetEquippedItem());
1365  }
1366  else
1367  {
1368  World->DigBlock(absPos);
1369  }
1370 
1371  // Damage the tool:
1373  m_Player->UseEquippedItem(dlAction);
1374 
1375  World->BroadcastSoundParticleEffect(EffectID::PARTICLE_SMOKE, absPos, a_OldBlock, this);
1376  blockHandler->OnPlayerBrokeBlock(ChunkInterface, *World, *m_Player, absPos, a_OldBlock, a_OldMeta);
1377  cRoot::Get()->GetPluginManager()->CallHookPlayerBrokenBlock(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_OldBlock, a_OldMeta);
1378 }
1379 
1380 
1381 
1382 
1383 
1385 {
1386  if (!m_HasStartedDigging) // Hasn't received the DIG_STARTED packet
1387  {
1388  return;
1389  }
1390 
1391  m_HasStartedDigging = false;
1392  if (m_BlockDigAnimStage != -1)
1393  {
1394  // End dig animation
1395  m_BlockDigAnimStage = -1;
1396  // It seems that 10 ends block animation
1398  }
1399 
1400  m_BlockDigAnimX = -1;
1401  m_BlockDigAnimY = -1;
1402  m_BlockDigAnimZ = -1;
1403 }
1404 
1405 
1406 
1407 
1408 
1409 void cClientHandle::HandleRightClick(int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, eHand a_Hand)
1410 {
1411  // This function handles three actions:
1412  // (1) Place a block;
1413  // (2) "Use" a block: Interactive with the block, like opening a chest/crafting table/furnace;
1414  // (3) Use the held item targeting a block. E.g. farming.
1415  //
1416  // Sneaking player will not use the block if hand is not empty.
1417  // Frozen player can do nothing.
1418  // In Game Mode Spectator, player cannot use item or place block, but can interactive with some block depending on cBlockInfo::IsUseableBySpectator(BlockType)
1419  //
1420  // If the action failed, we need to send an update of the placed block or inventory to the client.
1421  //
1422  // Actions rejected by plugin will not lead to other attempts.
1423  // E.g., when opening a chest with a dirt in hand, if the plugin rejects opening the chest, the dirt will not be placed.
1424 
1425  // TODO: We are still consuming the items in main hand. Remove this override when the off-hand consumption is handled correctly.
1426  a_Hand = eHand::hMain;
1427  const cItem & HeldItem = (a_Hand == eHand::hOff) ? m_Player->GetInventory().GetShieldSlot() : m_Player->GetEquippedItem();
1429 
1430  // TODO: This distance should be calculated from the point that the cursor pointing at, instead of the center of the block
1431  // Distance from the block's center to the player's eye height
1432  double Dist = (Vector3d(a_BlockX, a_BlockY, a_BlockZ) + Vector3d(0.5, 0.5, 0.5) - m_Player->GetEyePosition()).Length();
1433  FLOGD("HandleRightClick: {0}, face {1}, Hand: {2}, HeldItem: {3}; Dist: {4:.02f}",
1434  Vector3i{a_BlockX, a_BlockY, a_BlockZ}, a_BlockFace, a_Hand, ItemToFullString(HeldItem), Dist
1435  );
1436 
1437  // Check the reach distance:
1438  // _X 2014-11-25: I've maxed at 5.26 with a Survival client and 5.78 with a Creative client in my tests
1439  double MaxDist = m_Player->IsGameModeCreative() ? 5.78 : 5.26;
1440  bool IsWithinReach = (Dist <= MaxDist);
1441  cWorld * World = m_Player->GetWorld();
1442  cPluginManager * PlgMgr = cRoot::Get()->GetPluginManager();
1443 
1444  bool Success = false;
1445  if (
1446  !PlgMgr->CallHookPlayerRightClick(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ) &&
1447  IsWithinReach && !m_Player->IsFrozen()
1448  )
1449  {
1450  BLOCKTYPE BlockType;
1451  NIBBLETYPE BlockMeta;
1452  World->GetBlockTypeMeta(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta);
1454 
1455  bool Placeable = ItemHandler->IsPlaceable() && !m_Player->IsGameModeAdventure() && !m_Player->IsGameModeSpectator();
1456  bool BlockUsable = BlockHandler->IsUseable() && (!m_Player->IsGameModeSpectator() || cBlockInfo::IsUseableBySpectator(BlockType));
1457 
1458  if (BlockUsable && !(m_Player->IsCrouched() && !HeldItem.IsEmpty()))
1459  {
1460  // use a block
1461  cChunkInterface ChunkInterface(World->GetChunkMap());
1462  if (!PlgMgr->CallHookPlayerUsingBlock(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, BlockType, BlockMeta))
1463  {
1464  if (BlockHandler->OnUse(ChunkInterface, *World, *m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ))
1465  {
1466  // block use was successful, we're done
1467  PlgMgr->CallHookPlayerUsedBlock(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, BlockType, BlockMeta);
1468  Success = true;
1469  }
1470 
1471  // Check if the item is place able, for example a torch on a fence
1472  if (!Success && Placeable)
1473  {
1474  // place a block
1475  Success = ItemHandler->OnPlayerPlace(*World, *m_Player, HeldItem, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ);
1476  }
1477  }
1478  else
1479  {
1480  // TODO: OnCancelRightClick seems to do the same thing with updating blocks at the end of this function. Need to double check
1481  // A plugin doesn't agree with the action, replace the block on the client and quit:
1482  BlockHandler->OnCancelRightClick(ChunkInterface, *World, *m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace);
1483  }
1484  }
1485  else if (Placeable)
1486  {
1487  // TODO: Double check that we don't need this for using item and for packet out of range
1490  {
1491  Kick("Too many blocks were placed / interacted with per unit time - hacked client?");
1492  return;
1493  }
1494  // place a block
1495  Success = ItemHandler->OnPlayerPlace(*World, *m_Player, HeldItem, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ);
1496  }
1497  else
1498  {
1499  // Use an item in hand with a target block
1500  if (!PlgMgr->CallHookPlayerUsingItem(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ))
1501  {
1502  // All plugins agree with using the item
1503  cBlockInServerPluginInterface PluginInterface(*World);
1504  ItemHandler->OnItemUse(World, m_Player, PluginInterface, HeldItem, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace);
1505  PlgMgr->CallHookPlayerUsedItem(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ);
1506  Success = true;
1507  }
1508  }
1509  }
1510  if (!Success)
1511  {
1512  // Update the target block including the block above and below for 2 block high things
1513  AddFaceDirection(a_BlockX, a_BlockY, a_BlockZ, a_BlockFace);
1514  for (int y = a_BlockY - 1; y <= a_BlockY + 1; y++)
1515  {
1516  if (cChunkDef::IsValidHeight(y))
1517  {
1518  World->SendBlockTo(a_BlockX, y, a_BlockZ, *m_Player);
1519  }
1520  }
1521  // TODO: Send corresponding slot based on hand
1523  }
1524 }
1525 
1526 
1527 
1528 
1529 
1530 void cClientHandle::HandleChat(const AString & a_Message)
1531 {
1532  // We no longer need to postpone message processing, because the messages already arrive in the Tick thread
1533 
1534  // If a command, perform it:
1535  AString Message(a_Message);
1536  if (cRoot::Get()->GetServer()->Command(*this, Message))
1537  {
1538  return;
1539  }
1540 
1541  // Not a command, broadcast as a message:
1542  cCompositeChat Msg;
1543  AString Color = m_Player->GetColor();
1544  if (Color.length() == 3)
1545  {
1546  Color = AString("@") + Color[2];
1547  }
1548  else
1549  {
1550  Color.clear();
1551  }
1552  Msg.AddTextPart("<");
1553  Msg.ParseText(m_Player->GetPrefix());
1554  Msg.AddTextPart(m_Player->GetName(), Color);
1555  Msg.ParseText(m_Player->GetSuffix());
1556  Msg.AddTextPart("> ");
1557  Msg.ParseText(Message);
1558  Msg.UnderlineUrls();
1559  cRoot::Get()->BroadcastChat(Msg);
1560 }
1561 
1562 
1563 
1564 
1565 
1566 void cClientHandle::HandlePlayerLook(float a_Rotation, float a_Pitch, bool a_IsOnGround)
1567 {
1568  if ((m_Player == nullptr) || (m_State != csPlaying))
1569  {
1570  return;
1571  }
1572 
1573  m_Player->SetYaw (a_Rotation);
1574  m_Player->SetHeadYaw (a_Rotation);
1575  m_Player->SetPitch (a_Pitch);
1576  m_Player->SetTouchGround(a_IsOnGround);
1577 }
1578 
1579 
1580 
1581 
1582 
1583 void cClientHandle::HandlePlayerMoveLook(double a_PosX, double a_PosY, double a_PosZ, double a_Stance, float a_Rotation, float a_Pitch, bool a_IsOnGround)
1584 {
1585  HandlePlayerPos(a_PosX, a_PosY, a_PosZ, a_Stance, a_IsOnGround);
1586  HandlePlayerLook(a_Rotation, a_Pitch, a_IsOnGround);
1587 }
1588 
1589 
1590 
1591 
1592 
1593 void cClientHandle::HandleAnimation(int a_Animation)
1594 {
1595  if (cPluginManager::Get()->CallHookPlayerAnimation(*m_Player, a_Animation))
1596  {
1597  // Plugin disagrees, bail out
1598  return;
1599  }
1600 
1601  m_Player->GetWorld()->BroadcastEntityAnimation(*m_Player, static_cast<char>(a_Animation), this);
1602 }
1603 
1604 
1605 
1606 
1607 
1609 {
1612 }
1613 
1614 
1615 
1616 
1617 
1618 void cClientHandle::HandleSpectate(const cUUID & a_PlayerUUID)
1619 {
1620  m_Player->GetWorld()->DoWithPlayerByUUID(a_PlayerUUID, [=](cPlayer & a_ToSpectate)
1621  {
1622  m_Player->TeleportToEntity(a_ToSpectate);
1623  return true;
1624  });
1625 }
1626 
1627 
1628 
1629 
1630 
1631 void cClientHandle::HandleSteerVehicle(float a_Forward, float a_Sideways)
1632 {
1633  m_Player->SteerVehicle(a_Forward, a_Sideways);
1634 }
1635 
1636 
1637 
1638 
1639 
1641 {
1642  m_Player->CloseWindowIfID(static_cast<char>(a_WindowID));
1643 }
1644 
1645 
1646 
1647 
1648 
1649 void cClientHandle::HandleWindowClick(UInt8 a_WindowID, Int16 a_SlotNum, eClickAction a_ClickAction, const cItem & a_HeldItem)
1650 {
1651  LOGD("WindowClick: WinID %d, SlotNum %d, action: %s, Item %s x %d",
1652  a_WindowID, a_SlotNum, ClickActionToString(a_ClickAction),
1653  ItemToString(a_HeldItem).c_str(), a_HeldItem.m_ItemCount
1654  );
1655 
1656  cWindow * Window = m_Player->GetWindow();
1657  if (Window == nullptr)
1658  {
1659  LOGWARNING("Player \"%s\" clicked in a non-existent window. Ignoring", m_Username.c_str());
1660  return;
1661  }
1662 
1663  Window->Clicked(*m_Player, a_WindowID, a_SlotNum, a_ClickAction, a_HeldItem);
1664 }
1665 
1666 
1667 
1668 
1669 
1671  int a_BlockX, int a_BlockY, int a_BlockZ,
1672  const AString & a_Line1, const AString & a_Line2,
1673  const AString & a_Line3, const AString & a_Line4
1674 )
1675 {
1676  if (m_LastPlacedSign.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ)))
1677  {
1678  m_LastPlacedSign.Set(0, -1, 0);
1679  m_Player->GetWorld()->SetSignLines(a_BlockX, a_BlockY, a_BlockZ, a_Line1, a_Line2, a_Line3, a_Line4, m_Player);
1680  }
1681 }
1682 
1683 
1684 
1685 
1686 
1687 void cClientHandle::HandleUseEntity(UInt32 a_TargetEntityID, bool a_IsLeftClick)
1688 {
1689  // TODO: Let plugins interfere via a hook
1690 
1691  // If the player is a spectator, let him spectate
1692  if (m_Player->IsGameModeSpectator() && a_IsLeftClick)
1693  {
1694  m_Player->GetWorld()->DoWithEntityByID(a_TargetEntityID, [=](cEntity & a_Entity)
1695  {
1696  m_Player->AttachTo(&a_Entity);
1697  return true;
1698  });
1699  return;
1700  }
1701 
1702  // If it is a right click, call the entity's OnRightClicked() handler:
1703  if (!a_IsLeftClick)
1704  {
1705  cWorld * World = m_Player->GetWorld();
1706  World->DoWithEntityByID(a_TargetEntityID, [=](cEntity & a_Entity)
1707  {
1708  if (
1709  cPluginManager::Get()->CallHookPlayerRightClickingEntity(*m_Player, a_Entity) ||
1710  (
1711  m_Player->IsGameModeSpectator() && // Spectators cannot interact with every entity
1712  (
1713  !a_Entity.IsMinecart() || // They can only interact with minecarts
1714  (
1715  (static_cast<cMinecart &>(a_Entity).GetPayload() != cMinecart::mpChest) && // And only if the type matches a minecart with a chest or
1716  (static_cast<cMinecart &>(a_Entity).GetPayload() != cMinecart::mpHopper) // a minecart with a hopper
1717  )
1718  )
1719  )
1720  )
1721  {
1722  return false;
1723  }
1724  a_Entity.OnRightClicked(*m_Player);
1725  return false;
1726  }
1727  );
1728  return;
1729  }
1730 
1731  // If it is a left click, attack the entity:
1732  m_Player->GetWorld()->DoWithEntityByID(a_TargetEntityID, [=](cEntity & a_Entity)
1733  {
1734  if (!a_Entity.GetWorld()->IsPVPEnabled())
1735  {
1736  // PVP is disabled, disallow players hurting other players:
1737  if (a_Entity.IsPlayer())
1738  {
1739  // Player is hurting another player which is not allowed when PVP is disabled so ignore it
1740  return true;
1741  }
1742  }
1743  a_Entity.TakeDamage(*m_Player);
1745  if (a_Entity.IsPawn())
1746  {
1747  m_Player->NotifyNearbyWolves(static_cast<cPawn*>(&a_Entity), true);
1748  }
1749  return true;
1750  }
1751  );
1752 }
1753 
1754 
1755 
1756 
1757 
1759 {
1760  // Use the held item without targeting a block: eating, drinking, charging a bow, using buckets
1761  // In version 1.8.x, this function shares the same packet id with HandleRightClick.
1762  // In version >= 1.9, there is a new packet id for "Use Item".
1763 
1764  // TODO: We are still consuming the items in main hand. Remove this override when the off-hand consumption is handled correctly.
1765  a_Hand = eHand::hMain;
1766  const cItem & HeldItem = (a_Hand == eHand::hOff) ? m_Player->GetInventory().GetShieldSlot() : m_Player->GetEquippedItem();
1768  cWorld * World = m_Player->GetWorld();
1769  cPluginManager * PlgMgr = cRoot::Get()->GetPluginManager();
1770 
1771  LOGD("HandleUseItem: Hand: %d; HeldItem: %s", a_Hand, ItemToFullString(HeldItem).c_str());
1772 
1773  if (PlgMgr->CallHookPlayerRightClick(*m_Player, -1, 255, -1, BLOCK_FACE_NONE, 0, 0, 0))
1774  {
1775  return; // Plugin denied click action
1776  }
1777 
1778  // Use item in main / off hand
1779  // TODO: do we need to sync the current inventory with client if it fails?
1781  {
1782  return;
1783  }
1784 
1785  if (ItemHandler->IsFood() || ItemHandler->IsDrinkable(HeldItem.m_ItemDamage))
1786  {
1787  if (
1788  ItemHandler->IsFood() &&
1789  (m_Player->IsSatiated() || m_Player->IsGameModeCreative()) && // Only non-creative or hungry players can eat
1790  (HeldItem.m_ItemType != E_ITEM_GOLDEN_APPLE) // Golden apple is a special case, it is used instead of eaten
1791  )
1792  {
1793  // The player is satiated or in creative, and trying to eat
1794  return;
1795  }
1796  if (!PlgMgr->CallHookPlayerEating(*m_Player))
1797  {
1798  m_Player->StartEating();
1799  }
1800  }
1801  else
1802  {
1803  // Use an item in hand without a target block
1804  if (!PlgMgr->CallHookPlayerUsingItem(*m_Player, -1, 255, -1, BLOCK_FACE_NONE, 0, 0, 0))
1805  {
1806  // All plugins agree with using the item
1807  cBlockInServerPluginInterface PluginInterface(*World);
1808  ItemHandler->OnItemUse(World, m_Player, PluginInterface, HeldItem, -1, 255, -1, BLOCK_FACE_NONE);
1809  PlgMgr->CallHookPlayerUsedItem(*m_Player, -1, 255, -1, BLOCK_FACE_NONE, 0, 0, 0);
1810  }
1811  }
1812 }
1813 
1814 
1815 
1816 
1817 
1819 {
1820  if (m_Player == nullptr)
1821  {
1822  Destroy();
1823  return;
1824  }
1825  m_Player->Respawn();
1827 }
1828 
1829 
1830 
1831 
1832 
1834 {
1835  if (a_KeepAliveID == m_PingID)
1836  {
1837  m_Ping = std::chrono::steady_clock::now() - m_PingStartTime;
1838  }
1839 }
1840 
1841 
1842 
1843 
1844 
1845 bool cClientHandle::CheckMultiLogin(const AString & a_Username)
1846 {
1847  // If the multilogin is allowed, skip this check entirely:
1848  if ((cRoot::Get()->GetServer()->DoesAllowMultiLogin()))
1849  {
1850  return true;
1851  }
1852 
1853  // Check if the player is waiting to be transferred to the World.
1854  if (cRoot::Get()->GetServer()->IsPlayerInQueue(a_Username))
1855  {
1856  Kick("A player of the username is already logged in");
1857  return false;
1858  }
1859 
1860  // Check if the player is in any World.
1861  if (cRoot::Get()->DoWithPlayer(a_Username, [](cPlayer &) { return true; }))
1862  {
1863  Kick("A player of the username is already logged in");
1864  return false;
1865  }
1866  return true;
1867 }
1868 
1869 
1870 
1871 
1872 
1873 bool cClientHandle::HandleHandshake(const AString & a_Username)
1874 {
1875  if (a_Username.length() > 16)
1876  {
1877  Kick("Your username is too long (>16 characters)");
1878  return false;
1879  }
1880 
1881  if (cRoot::Get()->GetPluginManager()->CallHookHandshake(*this, a_Username))
1882  {
1883  Kick("Entry denied by plugin");
1884  return false;
1885  }
1886 
1887  return CheckMultiLogin(a_Username);
1888 }
1889 
1890 
1891 
1892 
1893 
1894 void cClientHandle::HandleEntityCrouch(UInt32 a_EntityID, bool a_IsCrouching)
1895 {
1896  if (a_EntityID != m_Player->GetUniqueID())
1897  {
1898  // We should only receive entity actions from the entity that is performing the action
1899  return;
1900  }
1901 
1902  m_Player->SetCrouch(a_IsCrouching);
1903 }
1904 
1905 
1906 
1907 
1908 
1910 {
1911  if (a_EntityID != m_Player->GetUniqueID())
1912  {
1913  // We should only receive entity actions from the entity that is performing the action
1914  return;
1915  }
1916 
1917  cChunkInterface Interface(GetPlayer()->GetWorld()->GetChunkMap());
1918  cBlockBedHandler::SetBedOccupationState(Interface, GetPlayer()->GetLastBedPos(), false);
1919  GetPlayer()->SetIsInBed(false);
1920 }
1921 
1922 
1923 
1924 
1925 
1926 void cClientHandle::HandleEntitySprinting(UInt32 a_EntityID, bool a_IsSprinting)
1927 {
1928  if (a_EntityID != m_Player->GetUniqueID())
1929  {
1930  // We should only receive entity actions from the entity that is performing the action
1931  return;
1932  }
1933 
1934  m_Player->SetSprint(a_IsSprinting);
1935 }
1936 
1937 
1938 
1939 
1940 
1942 {
1943  if (m_Player == nullptr)
1944  {
1945  return;
1946  }
1947  m_Player->Detach();
1948 }
1949 
1950 
1951 
1952 
1953 
1955 {
1956  AStringVector Results;
1957  // Get player name completions.
1958  if (cRoot::Get()->GetServer()->ShouldAllowMultiWorldTabCompletion())
1959  {
1960  Results = cRoot::Get()->GetPlayerTabCompletionMultiWorld(a_Text);
1961  }
1962  else
1963  {
1964  m_Player->GetWorld()->TabCompleteUserName(a_Text, Results);
1965  }
1966 
1967  // Get command completions.
1968  cRoot::Get()->GetPluginManager()->TabCompleteCommand(a_Text, Results, m_Player);
1969  if (Results.empty())
1970  {
1971  return;
1972  }
1973 
1974  // Sort and send results.
1975  std::sort(Results.begin(), Results.end());
1976  SendTabCompletionResults(Results);
1977 }
1978 
1979 
1980 
1981 
1982 
1983 void cClientHandle::SendData(const char * a_Data, size_t a_Size)
1984 {
1985  if (m_HasSentDC)
1986  {
1987  // This could crash the client, because they've already unloaded the world etc., and suddenly a wild packet appears (#31)
1988  return;
1989  }
1990 
1991  cCSLock Lock(m_CSOutgoingData);
1992  m_OutgoingData.append(a_Data, a_Size);
1993 }
1994 
1995 
1996 
1997 
1998 
2000 {
2001  // Remove all associated chunks:
2002  decltype(m_LoadedChunks) Chunks;
2003  {
2004  cCSLock Lock(m_CSChunkLists);
2005  std::swap(Chunks, m_LoadedChunks);
2006  m_ChunksToSend.clear();
2007  }
2008  for (auto && Chunk : Chunks)
2009  {
2010  SendUnloadChunk(Chunk.m_ChunkX, Chunk.m_ChunkZ);
2011  } // for itr - Chunks[]
2012 
2013  // Here, we set last streamed values to bogus ones so everything is resent
2014  m_LastStreamedChunkX = 0x7fffffff;
2015  m_LastStreamedChunkZ = 0x7fffffff;
2016 }
2017 
2018 
2019 
2020 
2021 
2023 {
2024  ASSERT(m_Player != nullptr);
2025  // Sets this to a junk value different from the player's current chunk, which invalidates it and
2026  // ensures its value will not be used.
2028 }
2029 
2030 
2031 
2032 
2033 
2035 {
2036  return m_HasSentPlayerChunk;
2037 }
2038 
2039 
2040 
2041 
2042 
2044 {
2045  ASSERT(m_Player != nullptr);
2046  ASSERT(m_Player->GetWorld() != nullptr);
2047 
2048  if ((cRoot::Get()->GetServer()->ShouldLimitPlayerBlockChanges()) && (m_NumBlockChangeInteractionsThisTick > MAX_BLOCK_CHANGE_INTERACTIONS))
2049  {
2050  return false;
2051  }
2052 
2053  return true;
2054 }
2055 
2056 
2057 
2058 
2059 
2060 void cClientHandle::Tick(float a_Dt)
2061 {
2062  // anticheat fastbreak
2063  if (m_HasStartedDigging)
2064  {
2067  }
2068 
2070 
2071  // If player has been kicked, terminate the connection:
2072  if (m_State == csKicked)
2073  {
2074  m_Link->Shutdown();
2075  }
2076 
2077  // If destruction is queued, destroy now:
2079  {
2080  LOGD("Client %s @ %s (%p) has been queued for destruction, destroying now.",
2081  m_Username.c_str(), m_IPString.c_str(), static_cast<void *>(this)
2082  );
2083  Destroy();
2084  return;
2085  }
2086 
2088  if (m_TicksSinceLastPacket > 600) // 30 seconds time-out
2089  {
2090  SendDisconnect("Nooooo!! You timed out! D: Come back!");
2091  return;
2092  }
2093 
2094  // Only process further if the player object is valid:
2095  if (m_Player == nullptr)
2096  {
2097  return;
2098  }
2099 
2100  // Freeze the player if they are standing in a chunk not yet sent to the client
2101  m_HasSentPlayerChunk = false;
2102  if (m_Player->GetParentChunk() != nullptr)
2103  {
2104  // If the chunk is invalid, it has definitely not been sent to the client yet
2105  if (m_Player->GetParentChunk()->IsValid())
2106  {
2107  // Before iterating m_SentChunks, see if the player's coords equal m_CachedSentChunk
2108  // If so, the chunk has been sent to the client. This is an optimization that saves an iteration of m_SentChunks.
2110  {
2111  m_HasSentPlayerChunk = true;
2112  }
2113  else
2114  {
2115  // This block is entered only when the player moves to a new chunk, invalidating the cached coords.
2116  // Otherwise the cached coords are used.
2117  cCSLock Lock(m_CSChunkLists);
2118  auto itr = std::find(m_SentChunks.begin(), m_SentChunks.end(), cChunkCoords(m_Player->GetChunkX(), m_Player->GetChunkZ()));
2119  if (itr != m_SentChunks.end())
2120  {
2121  m_CachedSentChunk = *itr;
2122  m_HasSentPlayerChunk = true;
2123  }
2124  }
2125  }
2126  }
2127 
2128  // If the chunk the player's in was just sent, spawn the player:
2129  {
2130  cCSLock lock(m_CSState);
2132  {
2133  m_Protocol->SendPlayerMoveLook();
2134  m_State = csPlaying;
2135  }
2136  } // lock(m_CSState)
2137 
2138  // Send a ping packet:
2139  if (m_State == csPlaying)
2140  {
2141  if ((m_PingStartTime + PING_TIME_MS <= std::chrono::steady_clock::now()))
2142  {
2143  m_PingID++;
2144  m_PingStartTime = std::chrono::steady_clock::now();
2145  m_Protocol->SendKeepAlive(m_PingID);
2146  }
2147  }
2148 
2150  {
2151  // Stream 4 chunks per tick
2152  for (int i = 0; i < 4; i++)
2153  {
2154  // Stream the next chunk
2155  if (StreamNextChunk())
2156  {
2157  // Streaming finished. All chunks are loaded.
2158  break;
2159  }
2160  }
2161 
2162  // Unload all chunks that are out of the view distance (every 5 seconds)
2163  if ((m_Player->GetWorld()->GetWorldAge() % 100) == 0)
2164  {
2166  }
2167  }
2168 
2169  // Handle block break animation:
2170  if (m_BlockDigAnimStage > -1)
2171  {
2172  int lastAnimVal = m_BlockDigAnimStage;
2173  m_BlockDigAnimStage += static_cast<int>(m_BlockDigAnimSpeed * a_Dt);
2174  if (m_BlockDigAnimStage > 9000)
2175  {
2176  m_BlockDigAnimStage = 9000;
2177  }
2178  if (m_BlockDigAnimStage / 1000 != lastAnimVal / 1000)
2179  {
2180  m_Player->GetWorld()->BroadcastBlockBreakAnimation(static_cast<UInt32>(m_UniqueID), {m_BlockDigAnimX, m_BlockDigAnimY, m_BlockDigAnimZ}, static_cast<char>(m_BlockDigAnimStage / 1000), this);
2181  }
2182  }
2183 
2184  // Reset explosion & block change counters:
2187 }
2188 
2189 
2190 
2191 
2192 
2194 {
2196 
2197  // If destruction is queued, destroy now:
2199  {
2200  LOGD("Client %s @ %s (%p) has been queued for destruction, destroying now.",
2201  m_Username.c_str(), m_IPString.c_str(), static_cast<void *>(this)
2202  );
2203  Destroy();
2204  return;
2205  }
2206 
2207  {
2208  cCSLock lock(m_CSState);
2209  if (m_State == csAuthenticated)
2210  {
2211  StreamNextChunk();
2212 
2213  // Remove the client handle from the server, it will be ticked from its cPlayer object from now on
2215 
2216  // Add the player to the world (start ticking from there):
2218  m_Player->Initialize(std::move(m_PlayerPtr), *(m_Player->GetWorld()));
2219  return;
2220  }
2221  } // lock(m_CSState)
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(int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType)
2262 {
2263  m_Protocol->SendBlockAction(a_BlockX, a_BlockY, a_BlockZ, a_Byte1, a_Byte2, a_BlockType);
2264 }
2265 
2266 
2267 
2268 
2269 
2270 void cClientHandle::SendBlockBreakAnim(UInt32 a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage)
2271 {
2272  m_Protocol->SendBlockBreakAnim(a_EntityID, a_BlockX, a_BlockY, a_BlockZ, a_Stage);
2273 }
2274 
2275 
2276 
2277 
2278 
2279 void cClientHandle::SendBlockChange(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
2280 {
2281  int ChunkX, ChunkZ = 0;
2282  cChunkDef::BlockToChunk(a_BlockX, a_BlockZ, ChunkX, ChunkZ);
2283  cChunkCoords ChunkCoords = cChunkCoords(ChunkX, ChunkZ);
2284 
2285  // Do not send block changes in chunks that weren't sent to the client yet:
2286  cCSLock Lock(m_CSChunkLists);
2287  if (std::find(m_SentChunks.begin(), m_SentChunks.end(), ChunkCoords) != m_SentChunks.end())
2288  {
2289  Lock.Unlock();
2290  m_Protocol->SendBlockChange(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta);
2291  }
2292 }
2293 
2294 
2295 
2296 
2297 
2298 void cClientHandle::SendBlockChanges(int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes)
2299 {
2300  ASSERT(!a_Changes.empty()); // We don't want to be sending empty change packets!
2301 
2302  // Do not send block changes in chunks that weren't sent to the client yet:
2303  cChunkCoords ChunkCoords = cChunkCoords(a_ChunkX, a_ChunkZ);
2304  cCSLock Lock(m_CSChunkLists);
2305  if (std::find(m_SentChunks.begin(), m_SentChunks.end(), ChunkCoords) != m_SentChunks.end())
2306  {
2307  Lock.Unlock();
2308  m_Protocol->SendBlockChanges(a_ChunkX, a_ChunkZ, a_Changes);
2309  }
2310 }
2311 
2312 
2313 
2314 
2315 
2317 {
2318  m_Protocol->SendCameraSetTo(a_Entity);
2319 }
2320 
2321 
2322 
2323 
2324 
2325 void cClientHandle::SendChat(const AString & a_Message, eMessageType a_ChatPrefix, const AString & a_AdditionalData)
2326 {
2327  cWorld * World = GetPlayer()->GetWorld();
2328  if (World == nullptr)
2329  {
2330  World = cRoot::Get()->GetWorld(GetPlayer()->GetLoadedWorldName());
2331  if (World == nullptr)
2332  {
2333  World = cRoot::Get()->GetDefaultWorld();
2334  }
2335  }
2336 
2337  bool ShouldUsePrefixes = World->ShouldUseChatPrefixes();
2338  AString Message = FormatMessageType(ShouldUsePrefixes, a_ChatPrefix, a_AdditionalData);
2339  m_Protocol->SendChat(Message.append(a_Message), ctChatBox, ShouldUsePrefixes);
2340 }
2341 
2342 
2343 
2344 
2345 
2347 {
2348  cWorld * World = GetPlayer()->GetWorld();
2349  if (World == nullptr)
2350  {
2351  World = cRoot::Get()->GetWorld(GetPlayer()->GetLoadedWorldName());
2352  if (World == nullptr)
2353  {
2354  World = cRoot::Get()->GetDefaultWorld();
2355  }
2356  }
2357 
2358  bool ShouldUsePrefixes = World->ShouldUseChatPrefixes();
2359  m_Protocol->SendChat(a_Message, ctChatBox, ShouldUsePrefixes);
2360 }
2361 
2362 
2363 
2364 
2365 
2366 void cClientHandle::SendChatRaw(const AString & a_MessageRaw, eChatType a_Type)
2367 {
2368  m_Protocol->SendChatRaw(a_MessageRaw, a_Type);
2369 }
2370 
2371 
2372 
2373 
2374 
2375 void cClientHandle::SendChatAboveActionBar(const AString & a_Message, eMessageType a_ChatPrefix, const AString & a_AdditionalData)
2376 {
2377  cWorld * World = GetPlayer()->GetWorld();
2378  if (World == nullptr)
2379  {
2380  World = cRoot::Get()->GetWorld(GetPlayer()->GetLoadedWorldName());
2381  if (World == nullptr)
2382  {
2383  World = cRoot::Get()->GetDefaultWorld();
2384  }
2385  }
2386 
2387  AString Message = FormatMessageType(World->ShouldUseChatPrefixes(), a_ChatPrefix, a_AdditionalData);
2388  m_Protocol->SendChat(Message.append(a_Message), ctAboveActionBar);
2389 }
2390 
2391 
2392 
2393 
2394 
2396 {
2397  m_Protocol->SendChat(a_Message, ctAboveActionBar, GetPlayer()->GetWorld()->ShouldUseChatPrefixes());
2398 }
2399 
2400 
2401 
2402 
2403 
2404 void cClientHandle::SendChatSystem(const AString & a_Message, eMessageType a_ChatPrefix, const AString & a_AdditionalData)
2405 {
2406  cWorld * World = GetPlayer()->GetWorld();
2407  if (World == nullptr)
2408  {
2409  World = cRoot::Get()->GetWorld(GetPlayer()->GetLoadedWorldName());
2410  if (World == nullptr)
2411  {
2412  World = cRoot::Get()->GetDefaultWorld();
2413  }
2414  }
2415 
2416  auto ShouldUsePrefixes = World->ShouldUseChatPrefixes();
2417  AString Message = FormatMessageType(ShouldUsePrefixes, a_ChatPrefix, a_AdditionalData);
2418  m_Protocol->SendChat(Message.append(a_Message), ctSystem, ShouldUsePrefixes);
2419 }
2420 
2421 
2422 
2423 
2424 
2426 {
2427  m_Protocol->SendChat(a_Message, ctSystem, GetPlayer()->GetWorld()->ShouldUseChatPrefixes());
2428 }
2429 
2430 
2431 
2432 
2433 
2434 void cClientHandle::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer)
2435 {
2436  ASSERT(m_Player != nullptr);
2437 
2438  // Check chunks being sent, erase them from m_ChunksToSend:
2439  bool Found = false;
2440  {
2441  cCSLock Lock(m_CSChunkLists);
2442  auto itr = m_ChunksToSend.find(cChunkCoords{a_ChunkX, a_ChunkZ});
2443  if (itr != m_ChunksToSend.end())
2444  {
2445  m_ChunksToSend.erase(itr);
2446  Found = true;
2447  }
2448  }
2449  if (!Found)
2450  {
2451  // This just sometimes happens. If you have a reliably replicatable situation for this, go ahead and fix it
2452  // It's not a big issue anyway, just means that some chunks may be compressed several times
2453  // LOGD("Refusing to send chunk [%d, %d] to client \"%s\" at [%d, %d].", ChunkX, ChunkZ, m_Username.c_str(), m_Player->GetChunkX(), m_Player->GetChunkZ());
2454  return;
2455  }
2456 
2457  if (m_Protocol == nullptr)
2458  {
2459  // TODO (#2588): investigate if and why this occurs
2460  return;
2461  }
2462 
2463  m_Protocol->SendChunkData(a_ChunkX, a_ChunkZ, a_Serializer);
2464 
2465  // Add the chunk to the list of chunks sent to the player:
2466  {
2467  cCSLock Lock(m_CSChunkLists);
2468  m_SentChunks.push_back(cChunkCoords(a_ChunkX, a_ChunkZ));
2469  }
2470 }
2471 
2472 
2473 
2474 
2475 
2476 void cClientHandle::SendCollectEntity(const cEntity & a_Entity, const cPlayer & a_Player, int a_Count)
2477 {
2478  m_Protocol->SendCollectEntity(a_Entity, a_Player, a_Count);
2479 }
2480 
2481 
2482 
2483 
2484 
2486 {
2487  m_Protocol->SendDestroyEntity(a_Entity);
2488 }
2489 
2490 
2491 
2492 
2493 
2494 void cClientHandle::SendDetachEntity(const cEntity & a_Entity, const cEntity & a_PreviousVehicle)
2495 {
2496  m_Protocol->SendDetachEntity(a_Entity, a_PreviousVehicle);
2497 }
2498 
2499 
2500 
2501 
2502 
2504 {
2505  // Destruction (Destroy()) is called when the client disconnects, not when a disconnect packet (or anything else) is sent
2506  // Otherwise, the cClientHandle instance is can be unexpectedly removed from the associated player - Core/#142
2507  if (!m_HasSentDC)
2508  {
2509  LOGD("Sending a DC: \"%s\"", StripColorCodes(a_Reason).c_str());
2510  m_Protocol->SendDisconnect(a_Reason);
2511  m_HasSentDC = true;
2512  // csKicked means m_Link will be shut down on the next tick. The
2513  // disconnect packet data is sent in the tick thread so the connection
2514  // is closed there after the data is sent.
2515  m_State = csKicked;
2516  }
2517 }
2518 
2519 
2520 
2521 
2522 
2523 void cClientHandle::SendEditSign(int a_BlockX, int a_BlockY, int a_BlockZ)
2524 {
2525  m_LastPlacedSign.Set(a_BlockX, a_BlockY, a_BlockZ);
2526  m_Protocol->SendEditSign(a_BlockX, a_BlockY, a_BlockZ);
2527 }
2528 
2529 
2530 
2531 
2532 
2533 void cClientHandle::SendEntityEffect(const cEntity & a_Entity, int a_EffectID, int a_Amplifier, int a_Duration)
2534 {
2535  m_Protocol->SendEntityEffect(a_Entity, a_EffectID, a_Amplifier, a_Duration);
2536 }
2537 
2538 
2539 
2540 
2541 
2542 void cClientHandle::SendEntityEquipment(const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item)
2543 {
2544  m_Protocol->SendEntityEquipment(a_Entity, a_SlotNum, a_Item);
2545 }
2546 
2547 
2548 
2549 
2550 
2552 {
2553  ASSERT(a_Entity.GetUniqueID() != m_Player->GetUniqueID()); // Must not send for self
2554 
2555  m_Protocol->SendEntityHeadLook(a_Entity);
2556 }
2557 
2558 
2559 
2560 
2561 
2563 {
2564  ASSERT(a_Entity.GetUniqueID() != m_Player->GetUniqueID()); // Must not send for self
2565 
2566  m_Protocol->SendEntityLook(a_Entity);
2567 }
2568 
2569 
2570 
2571 
2572 
2574 {
2575  m_Protocol->SendEntityMetadata(a_Entity);
2576 }
2577 
2578 
2579 
2580 
2581 
2582 void cClientHandle::SendEntityRelMove(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ)
2583 {
2584  ASSERT(a_Entity.GetUniqueID() != m_Player->GetUniqueID()); // Must not send for self
2585 
2586  m_Protocol->SendEntityRelMove(a_Entity, a_RelX, a_RelY, a_RelZ);
2587 }
2588 
2589 
2590 
2591 
2592 
2593 void cClientHandle::SendEntityRelMoveLook(const cEntity & a_Entity, char a_RelX, char a_RelY, char a_RelZ)
2594 {
2595  ASSERT(a_Entity.GetUniqueID() != m_Player->GetUniqueID()); // Must not send for self
2596 
2597  m_Protocol->SendEntityRelMoveLook(a_Entity, a_RelX, a_RelY, a_RelZ);
2598 }
2599 
2600 
2601 
2602 
2603 
2604 void cClientHandle::SendEntityStatus(const cEntity & a_Entity, char a_Status)
2605 {
2606  m_Protocol->SendEntityStatus(a_Entity, a_Status);
2607 }
2608 
2609 
2610 
2611 
2612 
2614 {
2615  m_Protocol->SendEntityVelocity(a_Entity);
2616 }
2617 
2618 
2619 
2620 
2621 
2622 void cClientHandle::SendExplosion(double a_BlockX, double a_BlockY, double a_BlockZ, float a_Radius, const cVector3iArray & a_BlocksAffected, const Vector3d & a_PlayerMotion)
2623 {
2625  {
2626  LOGD("Dropped an explosion!");
2627  return;
2628  }
2629 
2630  // Update the statistics:
2632 
2633  m_Protocol->SendExplosion(a_BlockX, a_BlockY, a_BlockZ, a_Radius, a_BlocksAffected, a_PlayerMotion);
2634 }
2635 
2636 
2637 
2638 
2639 
2641 {
2642  m_Protocol->SendGameMode(a_GameMode);
2643 }
2644 
2645 
2646 
2647 
2648 
2650 {
2651  m_Protocol->SendHealth();
2652 }
2653 
2654 
2655 
2656 
2657 
2659 {
2660  m_Protocol->SendHeldItemChange(a_ItemIndex);
2661 }
2662 
2663 
2664 
2665 
2666 
2668 {
2669  m_Protocol->SendHideTitle();
2670 }
2671 
2672 
2673 
2674 
2675 
2676 void cClientHandle::SendInventorySlot(char a_WindowID, short a_SlotNum, const cItem & a_Item)
2677 {
2678  m_Protocol->SendInventorySlot(a_WindowID, a_SlotNum, a_Item);
2679 }
2680 
2681 
2682 
2683 
2684 
2685 void cClientHandle::SendMapData(const cMap & a_Map, int a_DataStartX, int a_DataStartY)
2686 {
2687  m_Protocol->SendMapData(a_Map, a_DataStartX, a_DataStartY);
2688 }
2689 
2690 
2691 
2692 
2693 
2694 void cClientHandle::SendParticleEffect(const AString & a_ParticleName, float a_SrcX, float a_SrcY, float a_SrcZ, float a_OffsetX, float a_OffsetY, float a_OffsetZ, float a_ParticleData, int a_ParticleAmount)
2695 {
2696  m_Protocol->SendParticleEffect(a_ParticleName, a_SrcX, a_SrcY, a_SrcZ, a_OffsetX, a_OffsetY, a_OffsetZ, a_ParticleData, a_ParticleAmount);
2697 }
2698 
2699 
2700 
2701 
2702 
2703 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)
2704 {
2705  m_Protocol->SendParticleEffect(a_ParticleName, a_Src, a_Offset, a_ParticleData, a_ParticleAmount, a_Data);
2706 }
2707 
2708 
2709 
2710 
2711 
2713 {
2714  m_Protocol->SendPickupSpawn(a_Pickup);
2715 }
2716 
2717 
2718 
2719 
2720 
2722 {
2723  m_Protocol->SendPaintingSpawn(a_Painting);
2724 }
2725 
2726 
2727 
2728 
2729 
2730 void cClientHandle::SendEntityAnimation(const cEntity & a_Entity, char a_Animation)
2731 {
2732  m_Protocol->SendEntityAnimation(a_Entity, a_Animation);
2733 }
2734 
2735 
2736 
2737 
2738 
2740 {
2741  m_Protocol->SendPlayerAbilities();
2742 }
2743 
2744 
2745 
2746 
2747 
2749 {
2750  m_Protocol->SendPlayerListAddPlayer(a_Player);
2751 }
2752 
2753 
2754 
2755 
2756 
2758 {
2759  m_Protocol->SendPlayerListRemovePlayer(a_Player);
2760 }
2761 
2762 
2763 
2764 
2765 
2767 {
2768  m_Protocol->SendPlayerListUpdateGameMode(a_Player);
2769 }
2770 
2771 
2772 
2773 
2774 
2776 {
2777  m_Protocol->SendPlayerListUpdatePing(a_Player);
2778 }
2779 
2780 
2781 
2782 
2783 
2784 void cClientHandle::SendPlayerListUpdateDisplayName(const cPlayer & a_Player, const AString & a_CustomName)
2785 {
2786  m_Protocol->SendPlayerListUpdateDisplayName(a_Player, a_CustomName);
2787 }
2788 
2789 
2790 
2791 
2792 
2794 {
2795  m_Protocol->SendPlayerMaxSpeed();
2796 }
2797 
2798 
2799 
2800 
2801 
2803 {
2804  /*
2805  FLOGD("Sending PlayerMoveLook: {0:0.2f}, stance {1:0.2f}, OnGround: {2}",
2806  m_Player->GetPosition(), m_Player->GetStance(), m_Player->IsOnGround()
2807  );
2808  */
2809  m_Protocol->SendPlayerMoveLook();
2810 }
2811 
2812 
2813 
2814 
2815 
2817 {
2818  m_Protocol->SendPlayerPosition();
2819 }
2820 
2821 
2822 
2823 
2824 
2826 {
2827  if (a_Player.GetUniqueID() == m_Player->GetUniqueID())
2828  {
2829  // Do NOT send this packet to myself
2830  return;
2831  }
2832 
2833  LOGD("Spawning player \"%s\" on client \"%s\" @ %s",
2834  a_Player.GetName().c_str(), GetPlayer()->GetName().c_str(), GetIPString().c_str()
2835  );
2836 
2837  m_Protocol->SendPlayerSpawn(a_Player);
2838 }
2839 
2840 
2841 
2842 
2843 
2844 void cClientHandle::SendPluginMessage(const AString & a_Channel, const AString & a_Message)
2845 {
2846  m_Protocol->SendPluginMessage(a_Channel, a_Message);
2847 }
2848 
2849 
2850 
2851 
2852 
2853 void cClientHandle::SendRemoveEntityEffect(const cEntity & a_Entity, int a_EffectID)
2854 {
2855  m_Protocol->SendRemoveEntityEffect(a_Entity, a_EffectID);
2856 }
2857 
2858 
2859 
2860 
2861 
2863 {
2864  m_Protocol->SendResetTitle();
2865 }
2866 
2867 
2868 
2869 
2870 
2871 void cClientHandle::SendRespawn(eDimension a_Dimension, bool a_ShouldIgnoreDimensionChecks)
2872 {
2873  if ((!a_ShouldIgnoreDimensionChecks) && (a_Dimension == m_LastSentDimension))
2874  {
2875  // The client goes crazy if we send a respawn packet with the dimension of the current world
2876  // So we send a temporary one first.
2877  // This is not needed when the player dies, hence the a_ShouldIgnoreDimensionChecks flag.
2878  // a_ShouldIgnoreDimensionChecks is true only at cPlayer::respawn, which is called after
2879  // the player dies.
2880  eDimension TemporaryDimension = (a_Dimension == dimOverworld) ? dimNether : dimOverworld;
2881  m_Protocol->SendRespawn(TemporaryDimension);
2882  }
2883  m_Protocol->SendRespawn(a_Dimension);
2884  m_LastSentDimension = a_Dimension;
2885 }
2886 
2887 
2888 
2889 
2890 
2892 {
2893  m_Protocol->SendExperience();
2894 }
2895 
2896 
2897 
2898 
2899 
2901 {
2902  m_Protocol->SendExperienceOrb(a_ExpOrb);
2903 }
2904 
2905 
2906 
2907 
2908 
2909 void cClientHandle::SendScoreboardObjective(const AString & a_Name, const AString & a_DisplayName, Byte a_Mode)
2910 {
2911  m_Protocol->SendScoreboardObjective(a_Name, a_DisplayName, a_Mode);
2912 }
2913 
2914 
2915 
2916 
2917 
2918 void cClientHandle::SendScoreUpdate(const AString & a_Objective, const AString & a_Player, cObjective::Score a_Score, Byte a_Mode)
2919 {
2920  m_Protocol->SendScoreUpdate(a_Objective, a_Player, a_Score, a_Mode);
2921 }
2922 
2923 
2924 
2925 
2926 
2928 {
2929  m_Protocol->SendDisplayObjective(a_Objective, a_Display);
2930 }
2931 
2932 
2933 
2934 
2935 
2937 {
2938  m_Protocol->SendSetSubTitle(a_SubTitle);
2939 }
2940 
2941 
2942 
2943 
2944 
2946 {
2947  m_Protocol->SendSetRawSubTitle(a_SubTitle);
2948 }
2949 
2950 
2951 
2952 
2953 
2955 {
2956  m_Protocol->SendSetTitle(a_Title);
2957 }
2958 
2959 
2960 
2961 
2962 
2964 {
2965  m_Protocol->SendSetRawTitle(a_Title);
2966 }
2967 
2968 
2969 
2970 
2971 
2972 void cClientHandle::SendSoundEffect(const AString & a_SoundName, double a_X, double a_Y, double a_Z, float a_Volume, float a_Pitch)
2973 {
2974  LOG("SendSoundEffect with double args is deprecated, use version with vector position parameter.");
2975  SendSoundEffect(a_SoundName, {a_X, a_Y, a_Z}, a_Volume, a_Pitch);
2976 }
2977 
2978 
2979 
2980 
2981 
2982 void cClientHandle::SendSoundEffect(const AString & a_SoundName, Vector3d a_Position, float a_Volume, float a_Pitch)
2983 {
2984  m_Protocol->SendSoundEffect(a_SoundName, a_Position.x, a_Position.y, a_Position.z, a_Volume, a_Pitch);
2985 }
2986 
2987 
2988 
2989 
2990 
2991 void cClientHandle::SendSoundParticleEffect(const EffectID a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data)
2992 {
2993  m_Protocol->SendSoundParticleEffect(a_EffectID, a_SrcX, a_SrcY, a_SrcZ, a_Data);
2994 }
2995 
2996 
2997 
2998 
2999 
3001 {
3002  m_Protocol->SendSpawnFallingBlock(a_FallingBlock);
3003 }
3004 
3005 
3006 
3007 
3008 
3010 {
3011  m_Protocol->SendSpawnMob(a_Mob);
3012 }
3013 
3014 
3015 
3016 
3017 
3018 void cClientHandle::SendSpawnObject(const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch)
3019 {
3020  m_Protocol->SendSpawnObject(a_Entity, a_ObjectType, a_ObjectData, a_Yaw, a_Pitch);
3021 }
3022 
3023 
3024 
3025 
3026 
3027 void cClientHandle::SendSpawnVehicle(const cEntity & a_Vehicle, char a_VehicleType, char a_VehicleSubType) // VehicleSubType is specific to Minecarts
3028 {
3029  m_Protocol->SendSpawnVehicle(a_Vehicle, a_VehicleType, a_VehicleSubType);
3030 }
3031 
3032 
3033 
3034 
3035 
3037 {
3038  m_Protocol->SendStatistics(a_Manager);
3039 }
3040 
3041 
3042 
3043 
3044 
3046 {
3047  m_Protocol->SendTabCompletionResults(a_Results);
3048 }
3049 
3050 
3051 
3052 
3053 
3055 {
3056  m_Protocol->SendTeleportEntity(a_Entity);
3057 }
3058 
3059 
3060 
3061 
3062 
3063 void cClientHandle::SendThunderbolt(int a_BlockX, int a_BlockY, int a_BlockZ)
3064 {
3065  m_Protocol->SendThunderbolt(a_BlockX, a_BlockY, a_BlockZ);
3066 }
3067 
3068 
3069 
3070 
3071 
3072 void cClientHandle::SendTitleTimes(int a_FadeInTicks, int a_DisplayTicks, int a_FadeOutTicks)
3073 {
3074  m_Protocol->SendTitleTimes(a_FadeInTicks, a_DisplayTicks, a_FadeOutTicks);
3075 }
3076 
3077 
3078 
3079 
3080 
3081 void cClientHandle::SendTimeUpdate(Int64 a_WorldAge, Int64 a_TimeOfDay, bool a_DoDaylightCycle)
3082 {
3083  m_Protocol->SendTimeUpdate(a_WorldAge, a_TimeOfDay, a_DoDaylightCycle);
3084 }
3085 
3086 
3087 
3088 
3089 
3090 void cClientHandle::SendUnloadChunk(int a_ChunkX, int a_ChunkZ)
3091 {
3092  // Remove the chunk from the list of chunks sent to the client:
3093  {
3094  cCSLock Lock(m_CSChunkLists);
3095  m_SentChunks.remove(cChunkCoords(a_ChunkX, a_ChunkZ));
3096  }
3097 
3098  m_Protocol->SendUnloadChunk(a_ChunkX, a_ChunkZ);
3099 }
3100 
3101 
3102 
3103 
3104 
3106 {
3107  m_Protocol->SendUpdateBlockEntity(a_BlockEntity);
3108 }
3109 
3110 
3111 
3112 
3113 
3115  int a_BlockX, int a_BlockY, int a_BlockZ,
3116  const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4
3117 )
3118 {
3119  m_Protocol->SendUpdateSign(
3120  a_BlockX, a_BlockY, a_BlockZ,
3121  a_Line1, a_Line2, a_Line3, a_Line4
3122  );
3123 }
3124 
3125 
3126 
3127 
3128 
3129 void cClientHandle::SendUseBed(const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ)
3130 {
3131  m_Protocol->SendUseBed(a_Entity, a_BlockX, a_BlockY, a_BlockZ);
3132 }
3133 
3134 
3135 
3136 
3137 
3139 {
3140  m_Protocol->SendWeather(a_Weather);
3141 }
3142 
3143 
3144 
3145 
3146 
3148 {
3149  m_Protocol->SendWholeInventory(a_Window);
3150 }
3151 
3152 
3153 
3154 
3155 
3157 {
3158  m_Protocol->SendWindowClose(a_Window);
3159 }
3160 
3161 
3162 
3163 
3164 
3166 {
3167  m_Protocol->SendWindowOpen(a_Window);
3168 }
3169 
3170 
3171 
3172 
3173 
3174 void cClientHandle::SendWindowProperty(const cWindow & a_Window, short a_Property, short a_Value)
3175 {
3176  m_Protocol->SendWindowProperty(a_Window, a_Property, a_Value);
3177 }
3178 
3179 
3180 
3181 
3182 
3184 {
3185  return m_Username;
3186 }
3187 
3188 
3189 
3190 
3191 
3192 void cClientHandle::SetUsername( const AString & a_Username)
3193 {
3194  m_Username = a_Username;
3195 }
3196 
3197 
3198 
3199 
3200 
3201 void cClientHandle::SetViewDistance(int a_ViewDistance)
3202 {
3203  m_RequestedViewDistance = a_ViewDistance;
3204  LOGD("%s is requesting ViewDistance of %d!", GetUsername().c_str(), m_RequestedViewDistance);
3205 
3206  // Set the current view distance based on the requested VD and world max VD:
3207  cWorld * world = m_Player->GetWorld();
3208  if (world != nullptr)
3209  {
3211  }
3212 }
3213 
3214 
3215 
3216 
3217 
3218 bool cClientHandle::HasPluginChannel(const AString & a_PluginChannel)
3219 {
3220  return (m_PluginChannels.find(a_PluginChannel) != m_PluginChannels.end());
3221 }
3222 
3223 
3224 
3225 
3226 
3227 bool cClientHandle::WantsSendChunk(int a_ChunkX, int a_ChunkZ)
3228 {
3230  {
3231  return false;
3232  }
3233 
3234  cCSLock Lock(m_CSChunkLists);
3235  return m_ChunksToSend.find(cChunkCoords(a_ChunkX, a_ChunkZ)) != m_ChunksToSend.end();
3236 }
3237 
3238 
3239 
3240 
3241 
3242 void cClientHandle::AddWantedChunk(int a_ChunkX, int a_ChunkZ)
3243 {
3245  {
3246  return;
3247  }
3248 
3249  LOGD("Adding chunk [%d, %d] to wanted chunks for client %p", a_ChunkX, a_ChunkZ, static_cast<void *>(this));
3250  cCSLock Lock(m_CSChunkLists);
3251  if (m_ChunksToSend.find(cChunkCoords(a_ChunkX, a_ChunkZ)) == m_ChunksToSend.end())
3252  {
3253  m_ChunksToSend.emplace(a_ChunkX, a_ChunkZ);
3254  }
3255 }
3256 
3257 
3258 
3259 
3260 
3262 {
3263  // Too much data in the incoming queue, the server is probably too busy, kick the client:
3264  LOGERROR("Too much data in queue for client \"%s\" @ %s, kicking them.", m_Username.c_str(), m_IPString.c_str());
3265  SendDisconnect("Server busy");
3266 }
3267 
3268 
3269 
3270 
3271 
3273 {
3274  LOGERROR("Unknown packet type 0x%x from client \"%s\" @ %s", a_PacketType, m_Username.c_str(), m_IPString.c_str());
3275 
3276  AString Reason;
3277  Printf(Reason, "Unknown [C->S] PacketType: 0x%x", a_PacketType);
3278  SendDisconnect(Reason);
3279 }
3280 
3281 
3282 
3283 
3284 
3286 {
3287  LOGERROR("Protocol error while parsing packet type 0x%02x; disconnecting client \"%s\"", a_PacketType, m_Username.c_str());
3288  SendDisconnect("Protocol error");
3289 }
3290 
3291 
3292 
3293 
3294 
3296 {
3297  // The socket has been closed for any reason
3298  /*
3299  LOGD("SocketClosed for client %s @ %s (%p), state = %d, m_Player = %p",
3300  m_Username.c_str(), m_IPString.c_str(), static_cast<void *>(this), m_State.load(), static_cast<void *>(m_Player)
3301  );
3302  //*/
3303 
3304  // Log into console, unless it's a client ping:
3305  if (!m_Username.empty())
3306  {
3307  LOGD("Client %s @ %s disconnected", m_Username.c_str(), m_IPString.c_str());
3308  cRoot::Get()->GetPluginManager()->CallHookDisconnect(*this, "Player disconnected");
3309  }
3310 
3311  // Queue self for destruction:
3312  cCSLock lock(m_CSState);
3314 }
3315 
3316 
3317 
3318 
3319 
3321 {
3322  ASSERT(m_Self == nullptr);
3323  m_Self = a_Self;
3324 }
3325 
3326 
3327 
3328 
3329 
3331 {
3332  // Process received network data:
3333  AString IncomingData;
3334  {
3335  cCSLock Lock(m_CSIncomingData);
3336  std::swap(IncomingData, m_IncomingData);
3337  }
3338  if (!IncomingData.empty())
3339  {
3340  m_Protocol->DataReceived(IncomingData.data(), IncomingData.size());
3341  }
3342 
3343  // Send any queued outgoing data:
3344  AString OutgoingData;
3345  {
3346  cCSLock Lock(m_CSOutgoingData);
3347  std::swap(OutgoingData, m_OutgoingData);
3348  }
3349  auto link = m_Link;
3350  if ((link != nullptr) && !OutgoingData.empty())
3351  {
3352  link->Send(OutgoingData.data(), OutgoingData.size());
3353  }
3354 }
3355 
3356 
3357 
3358 
3359 
3361 {
3362  m_Link = a_Link;
3363 }
3364 
3365 
3366 
3367 
3368 
3369 void cClientHandle::OnReceivedData(const char * a_Data, size_t a_Length)
3370 {
3371  // Reset the timeout:
3373 
3374  // Queue the incoming data to be processed in the tick thread:
3375  cCSLock Lock(m_CSIncomingData);
3376  m_IncomingData.append(a_Data, a_Length);
3377 }
3378 
3379 
3380 
3381 
3382 
3384 {
3385  /*
3386  LOGD("Client socket for %s @ %s has been closed.",
3387  m_Username.c_str(), m_IPString.c_str()
3388  );
3389  //*/
3390  {
3391  cCSLock Lock(m_CSOutgoingData);
3392  m_Link.reset();
3393  }
3394  SocketClosed();
3395 }
3396 
3397 
3398 
3399 
3400 
3401 void cClientHandle::OnError(int a_ErrorCode, const AString & a_ErrorMsg)
3402 {
3403  LOGD("An error has occurred on client link for %s @ %s: %d (%s). Client disconnected.",
3404  m_Username.c_str(), m_IPString.c_str(), a_ErrorCode, a_ErrorMsg.c_str()
3405  );
3406  {
3407  cCSLock Lock(m_CSOutgoingData);
3408  m_Link.reset();
3409  }
3410  SocketClosed();
3411 }
cCriticalSection m_CSChunkLists
Definition: ClientHandle.h:427
void SetSprint(bool a_IsSprinting)
Starts or stops sprinting, sends the max speed update to the client, if needed.
Definition: Player.cpp:884
int GetChunkZ(void) const
Definition: Entity.h:219
void SendSlot(cPlayer &a_Player, cSlotArea *a_SlotArea, int a_RelativeSlotNum)
Used by cSlotAreas to send individual slots to clients, a_RelativeSlotNum is the slot number relative...
Definition: Window.cpp:441
virtual bool IsDaylightCycleEnabled(void) const
Is the daylight cycle enabled?
Definition: World.h:100
eType
All types of entity effects (numbers correspond to protocol / storage types)
Definition: EntityEffect.h:11
AString m_Username
Definition: ClientHandle.h:423
void SetCanFly(bool a_CanFly)
If true the player can fly even when he&#39;s not in creative.
Definition: Player.cpp:900
void AddTextPart(const AString &a_Message, const AString &a_Style="")
Adds a plain text part, with optional style.
double GetPosY(void) const
Definition: Entity.h:207
virtual void OnError(int a_ErrorCode, const AString &a_ErrorMsg) override
Called when an error is detected on the connection.
void SetFlying(bool a_IsFlying)
Flags the player as flying.
Definition: Player.cpp:968
bool IsValid(void) const
Returns true iff the chunk block data is valid (loaded / generated)
Definition: Chunk.h:72
cChunkCoordsList m_SentChunks
Definition: ClientHandle.h:430
void HandleCreativeInventory(Int16 a_SlotNum, const cItem &a_HeldItem, eClickAction a_ClickAction)
Called when the client clicks the creative inventory window.
void HandleWindowClick(UInt8 a_WindowID, Int16 a_SlotNum, eClickAction a_ClickAction, const cItem &a_HeldItem)
cClientHandlePtr m_Self
Shared pointer to self, so that this instance can keep itself alive when needed.
Definition: ClientHandle.h:557
std::atomic< int > m_TicksSinceLastPacket
Number of ticks since the last network packet was received (increased in Tick(), reset in OnReceivedD...
Definition: ClientHandle.h:469
bool DigBlock(Vector3i a_BlockPos)
Replaces the specified block with air, and calls the apropriate block handlers (OnBreaking(), OnBroken()).
Definition: World.cpp:2180
The client has been destroyed, the destructor is to be called from the owner thread.
Definition: ClientHandle.h:504
std::unordered_set< cChunkCoords, cChunkCoordsHash > m_LoadedChunks
Definition: ClientHandle.h:428
void UnregisterPluginChannels(const AStringVector &a_ChannelList)
Removes all of the channels from the list of current plugin channels.
void SendSetRawSubTitle(const AString &a_SubTitle)
void SendSetTitle(const cCompositeChat &a_Title)
void SendParticleEffect(const AString &a_ParticleName, float a_SrcX, float a_SrcY, float a_SrcZ, float a_OffsetX, float a_OffsetY, float a_OffsetZ, float a_ParticleData, int a_ParticleAmount)
virtual void BroadcastEntityAnimation(const cEntity &a_Entity, Int8 a_Animation, const cClientHandle *a_Exclude=nullptr) override
void SendMessage(const AString &a_Message)
Definition: Player.cpp:1408
eGameMode
Definition: Defines.h:116
double GetPosX(void) const
Definition: Entity.h:206
The client is waiting for chunks, we&#39;re waiting for the loader to provide and send them...
Definition: ClientHandle.h:498
void HandlePing(void)
eDimension
Dimension of a world.
Definition: BlockID.h:1127
BLOCKTYPE GetBlock(Vector3i a_BlockPos)
Returns the block type at the specified position.
Definition: World.h:416
void PlayerDestroyed()
Notifies the server that a player is being destroyed; the server uses this to adjust the number of pl...
Definition: Server.cpp:157
std::chrono::steady_clock::time_point m_PingStartTime
Time of the last ping request sent to the client.
Definition: ClientHandle.h:478
AStringVector BreakApartPluginChannels(const AString &a_PluginChannels)
Converts the protocol-formatted channel list (NUL-separated) into a proper string vector...
T x
Definition: Vector3.h:17
void HandleRespawn(void)
void HandleEnchantItem(UInt8 a_WindowID, UInt8 a_Enchantment)
Called when the player enchants an Item in the Enchanting table UI.
void HandleKeepAlive(UInt32 a_KeepAliveID)
cTeam * UpdateTeam(void)
Forces the player to query the scoreboard for his team.
Definition: Player.cpp:1329
void SendChatSystem(const AString &a_Message, eMessageType a_ChatPrefix, const AString &a_AdditionalData="")
eWeather
Definition: Defines.h:151
virtual bool IsCrouched(void) const override
Definition: Player.h:568
void OpenHorseInventory()
Opens the inventory of any tame horse the player is riding.
Definition: Player.cpp:2270
void SendAttachEntity(const cEntity &a_Entity, const cEntity &a_Vehicle)
Definition: ExpOrb.h:11
void SendEntityVelocity(const cEntity &a_Entity)
void SendMapData(const cMap &a_Map, int a_DataStartX, int a_DataStartY)
void HandleAnimation(int a_Animation)
AString StripColorCodes(const AString &a_Message)
Removes all control codes used by MC for colors and styles.
bool Equals(const Vector3< T > &a_Rhs) const
Definition: Vector3.h:135
void BroadcastChat(const AString &a_Message, eMessageType a_ChatPrefix=mtCustom)
Sends a chat message to all connected clients (in all worlds)
Definition: Root.cpp:830
static const char * Gray
Definition: ChatColor.h:25
Definition: Defines.h:209
virtual bool OnItemUse(cWorld *a_World, cPlayer *a_Player, cBlockPluginInterface &a_PluginInterface, const cItem &a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace)
Called when the player tries to use the item (right mouse button).
bool ShouldUseChatPrefixes(void) const
Definition: World.h:897
void SendDisplayObjective(const AString &a_Objective, cScoreboard::eDisplaySlot a_Display)
void HandlePlayerAbilities(bool a_CanFly, bool a_IsFlying, float FlyingSpeed, float WalkingSpeed)
bool CallHookPluginMessage(cClientHandle &a_Client, const AString &a_Channel, const AString &a_Message)
void RemoveClientFromChunkSender(cClientHandle *a_Client)
Removes client from ChunkSender&#39;s queue of chunks to be sent.
Definition: World.cpp:2816
Class that manages the statistics and achievements of a single player.
Definition: Statistics.h:127
bool CallHookDisconnect(cClientHandle &a_Client, const AString &a_Reason)
cChunk * GetParentChunk()
Returns the chunk responsible for ticking this entity.
Definition: Entity.h:540
void SendScoreboardObjective(const AString &a_Name, const AString &a_DisplayName, Byte a_Mode)
short m_ItemDamage
Definition: Item.h:211
void HandleUnmount(void)
bool IsSword(short a_ItemID)
Definition: Defines.h:1043
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:199
bool CallHookPlayerUsedItem(cPlayer &a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ)
virtual void BroadcastEntityEquipment(const cEntity &a_Entity, short a_SlotNum, const cItem &a_Item, const cClientHandle *a_Exclude=nullptr) override
int GetEquippedSlotNum(void)
Returns slot number of equiped item.
Definition: Inventory.h:149
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 HandleOpenHorseInventory(UInt32 a_EntityID)
Handles a player opening their inventory while riding a horse.
void SendChatRaw(const AString &a_MessageRaw, eChatType a_Type)
signed short Int16
Definition: Globals.h:109
virtual void OnItemShoot(cPlayer *, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace)
Called when the client sends the SHOOT status in the lclk packet.
Definition: ItemHandler.h:86
void UnloadOutOfRangeChunks(void)
Remove all loaded chunks that are no longer in range.
int GetWindowType(void) const
Definition: Window.h:81
AString GetPrefix(void) const
Returns the player name prefix, may contain @ format directives.
Definition: Player.cpp:1870
unsigned char BLOCKTYPE
The datatype used by blockdata.
Definition: ChunkDef.h:42
void SendEntityLook(const cEntity &a_Entity)
void UnderlineUrls(void)
Adds the "underline" style to each part that is an URL.
Encapsulates an in-game world map.
Definition: Map.h:80
void ParseText(const AString &a_ParseText)
Parses text into various parts, adds those.
cForgeHandshake m_ForgeHandshake
Forge handshake state machine.
Definition: ClientHandle.h:410
void Tick(float a_Dt)
Called while the client is being ticked from the world via its cPlayer object.
bool SetSignLines(int a_BlockX, int a_BlockY, int a_BlockZ, 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:2852
std::unique_ptr< cPlayer > m_PlayerPtr
Definition: ClientHandle.h:453
bool CallHookPlayerUsingBlock(cPlayer &a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
void SocketClosed(void)
Called when the network socket has been closed.
void SendHeldItemChange(int a_ItemIndex)
The client has logged in, waiting for external authentication.
Definition: ClientHandle.h:496
static cBlockHandler * GetHandler(BLOCKTYPE a_Type)
Definition: BlockInfo.h:57
eChatType
Definition: Defines.h:140
void SendSpawnFallingBlock(const cFallingBlock &a_FallingBlock)
bool CallHookPlayerLeftClick(cPlayer &a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, char a_Status)
static float FASTBREAK_PERCENTAGE
The percentage how much a block has to be broken.
Definition: ClientHandle.h:62
cWindow * GetWindow(void)
Definition: Player.h:239
void SendEditSign(int a_BlockX, int a_BlockY, int a_BlockZ)
void SetPosition(double a_PosX, double a_PosY, double a_PosZ)
Definition: Entity.h:226
virtual void BroadcastBlockBreakAnimation(UInt32 a_EntityID, Vector3i a_BlockPos, Int8 a_Stage, const cClientHandle *a_Exclude=nullptr) override
static const int Width
Definition: ChunkDef.h:134
void StartEating(void)
Starts eating the currently equipped item.
Definition: Player.cpp:684
#define FAST_FLOOR_DIV(x, div)
Faster than (int)floorf((float)x / (float)div)
Definition: Globals.h:297
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 SendPlayerSpawn(const cPlayer &a_Player)
void TossEquippedItem(char a_Amount=1)
tosses the item in the selected hotbar slot
Definition: Player.cpp:1923
void HandleRightClick(int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, eHand a_Hand)
static bool IsValidHeight(int a_Height)
Validates a height-coordinate.
Definition: ChunkDef.h:212
static AString FormatChatPrefix(bool ShouldAppendChatPrefixes, AString a_ChatPrefixS, AString m_Color1, AString m_Color2)
const cItem & GetEquippedItem(void) const
Definition: Player.h:142
void SetHeadYaw(double a_HeadYaw)
Definition: Entity.cpp:2027
bool m_HasSentPlayerChunk
Set to true when the chunk where the player is is sent to the client.
Definition: ClientHandle.h:535
Definition: Player.h:27
void SendInventorySlot(char a_WindowID, short a_SlotNum, const cItem &a_Item)
void SendDetachEntity(const cEntity &a_Entity, const cEntity &a_PreviousVehicle)
Definition: Defines.h:208
void RemoveClientFromChunks(cClientHandle *a_Client)
Removes the client from all chunks it is present in.
Definition: World.cpp:2788
int GetMaxViewDistance(void) const
Definition: World.h:894
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 IsEmpty(void) const
Definition: Item.h:116
void SendBlockBreakAnim(UInt32 a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage)
bool IsPlayerChunkSent()
static bool IsUseableBySpectator(BLOCKTYPE a_Type)
Definition: BlockInfo.h:49
virtual void Detach(void) override
Detaches from the currently attached entity, if any.
Definition: Player.cpp:2875
void SendSpawnObject(const cEntity &a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch)
void SendLeashEntity(const cEntity &a_Entity, const cEntity &a_EntityLeashedTo)
void PacketUnknown(UInt32 a_PacketType)
void HandleSteerVehicle(float Forward, float Sideways)
bool IsPVPEnabled(void) const
Definition: World.h:143
static cItemHandler * GetItemHandler(int a_ItemType)
Definition: ItemHandler.cpp:74
static const char * Delimiter
Definition: ChatColor.h:12
void SendPlayerListRemovePlayer(const cPlayer &a_Player)
virtual void OnRemoteClosed(void) override
Called when the remote end closes the connection.
void SendWholeInventory(const cWindow &a_Window)
cWorld * GetWorld(const AString &a_WorldName)
Returns a pointer to the world specified.
Definition: Root.cpp:641
UInt8 Version() const
Returns the version number of the UUID.
Definition: UUID.cpp:170
AString GetSuffix(void) const
Returns the player name suffix, may contain @ format directives.
Definition: Player.cpp:1879
bool m_HasStartedDigging
Definition: ClientHandle.h:488
int m_BlockDigAnimStage
Definition: ClientHandle.h:481
virtual bool OnUse(cChunkInterface &a_ChunkInterface, cWorldInterface &a_WorldInterface, cPlayer &a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ)
Called if the user right clicks the block and the block is useable returns true if the use was succes...
Definition: BlockHandler.h:115
void HandleWindowClose(UInt8 a_WindowID)
void SendPlayerMaxSpeed(void)
Informs the client of the maximum player speed (1.6.1+)
bool HasPluginChannel(const AString &a_PluginChannel)
void Set(T a_x, T a_y, T a_z)
Definition: Vector3.h:37
void SendBlockChanges(int a_ChunkX, int a_ChunkZ, const sSetBlockVector &a_Changes)
virtual bool IsOnGround(void) const override
Returns whether the entity is on ground or not.
Definition: Player.h:134
void SendSoundEffect(const AString &a_SoundName, double a_X, double a_Y, double a_Z, float a_Volume, float a_Pitch)
static void SetBedOccupationState(cChunkInterface &a_ChunkInterface, Vector3i a_BedPosition, bool a_IsOccupied)
Definition: BlockBed.h:78
void SetPitch(double a_Pitch)
Definition: Entity.cpp:2070
const AString & GetDescription(void) const
Definition: Server.h:65
const AString & GetName(void) const
Definition: Player.h:277
unsigned char NIBBLETYPE
The datatype used by nibbledata (meta, light, skylight)
Definition: ChunkDef.h:45
void DataReceived(cClientHandle *a_Client, const char *a_Data, size_t a_Size)
Process received data from the client advancing the Forge handshake.
Definition: Pickup.h:18
std::vector< Vector3i > cVector3iArray
Definition: Vector3.h:454
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 ItemToString(const cItem &a_Item)
Translates a full item into a string.
Definition: BlockID.cpp:257
virtual cItem GetOffHandEquipedItem(void) const override
Returns the currently offhand equipped item; empty item if none.
Definition: Player.h:75
eWeather GetWeather(void) const
Returns the current weather.
Definition: World.h:991
Constants used throughout the code, useful typedefs and utility functions.
Definition: ChunkDef.h:130
Vector3i m_LastPlacedSign
The positions from the last sign that the player placed.
Definition: ClientHandle.h:541
static const char * Red
Definition: ChatColor.h:21
bool HandleHandshake(const AString &a_Username)
Called when the protocol handshake has been received (for protocol versions that support it; otherwis...
void HandleCommandBlockBlockChange(int a_BlockX, int a_BlockY, int a_BlockZ, const AString &a_NewCommand)
Called when the protocol receives a MC|AdvCdm plugin message, indicating that the player set a new co...
cPlayer * m_Player
Definition: ClientHandle.h:450
void SetSelf(cClientHandlePtr a_Self)
Called right after the instance is created to store its SharedPtr inside.
void HandleTabCompletion(const AString &a_Text)
bool DropBlockAsPickups(Vector3i a_BlockPos, const cEntity *a_Digger=nullptr, const cItem *a_Tool=nullptr)
Digs the specified block, and spawns the appropriate pickups for it.
Definition: World.cpp:2200
cChannels m_PluginChannels
The plugin channels that the client has registered.
Definition: ClientHandle.h:544
virtual bool OnDiggingBlock(cWorld *a_World, cPlayer *a_Player, const cItem &a_HeldItem, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace)
Called while the player digs a block using this item.
void HandlePlayerPos(double a_PosX, double a_PosY, double a_PosZ, double a_Stance, bool a_IsOnGround)
Verifies and sets player position, performing relevant checks Calls relevant methods to process movem...
void SendWindowProperty(const cWindow &a_Window, short a_Property, short a_Value)
void SendSoundParticleEffect(const EffectID a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data)
void SendTabCompletionResults(const AStringVector &a_Results)
AString GetColor(void) const
Returns the full color code to use for this player, based on their rank.
Definition: Player.cpp:1854
static cPluginManager * Get(void)
Returns the instance of the Plugin Manager (there is only ever one)
void SetShieldSlot(const cItem &a_Item)
Sets current item in shield slot.
Definition: Inventory.cpp:294
void SendExplosion(double a_BlockX, double a_BlockY, double a_BlockZ, float a_Radius, const cVector3iArray &a_BlocksAffected, const Vector3d &a_PlayerMotion)
static const char * Rose
Definition: ChatColor.h:29
void RemoveFromAllChunks(void)
Removes the client from all chunks.
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:1395
void LOGERROR(const char *a_Format, fmt::ArgList a_ArgList)
Definition: Logger.cpp:183
void SendBlockAction(int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType)
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 SendSpawnMob(const cMonster &a_Mob)
void PacketError(UInt32 a_PacketType)
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:2738
T y
Definition: Vector3.h:17
bool StreamNextChunk()
This function sends a new unloaded chunk to the player.
Normal gameplay.
Definition: ClientHandle.h:500
void SetIsInBed(bool a_Flag)
Sets a player&#39;s in-bed state We can&#39;t be sure plugins will keep this value updated, so no exporting If value is false (not in bed), will update players of the fact that they have been ejected from the bed.
Definition: Player.h:363
void HandlePlayerMoveLook(double a_PosX, double a_PosY, double a_PosZ, double a_Stance, float a_Rotation, float a_Pitch, bool a_IsOnGround)
void HandlePlayerLook(float a_Rotation, float a_Pitch, bool a_IsOnGround)
void SendData(const char *a_Data, size_t a_Size)
void SendWeather(eWeather a_Weather)
static const int MIN_VIEW_DISTANCE
Definition: ClientHandle.h:57
const cItem & GetEquippedItem(void) const
Returns current equiped item.
Definition: Inventory.cpp:386
void SendTeleportEntity(const cEntity &a_Entity)
void SetTouchGround(bool a_bTouchGround)
Definition: Player.cpp:583
void RemoveFromWorld(void)
Called when the player moves into a different world.
virtual void OnReceivedData(const char *a_Data, size_t a_Length) override
Called when there&#39;s data incoming from the remote peer.
void SendCameraSetTo(const cEntity &a_Entity)
void SendExperience(void)
virtual void TeleportToEntity(cEntity &a_Entity)
Teleports to the entity specified.
Definition: Entity.cpp:1855
T z
Definition: Vector3.h:17
void SendPlayerLists(cPlayer *a_DestPlayer)
Send playerlist of all worlds to player.
Definition: Root.cpp:794
void FinishDigAnimation()
The clients will receive a finished dig animation.
int m_NumBlockChangeInteractionsThisTick
Number of place or break interactions this tick.
Definition: ClientHandle.h:524
bool IsPawn(void) const
Definition: Entity.h:174
void ClientMovedToWorld(const cClientHandle *a_Client)
Don&#39;t tick a_Client anymore, it will be ticked from its cPlayer instead.
Definition: Server.cpp:138
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:234
int m_LastStreamedChunkX
Definition: ClientHandle.h:465
Definition: UUID.h:10
Definition: Server.h:55
void SendRespawn(eDimension a_Dimension, bool a_ShouldIgnoreDimensionChecks=false)
eClickAction
Individual actions sent in the WindowClick packet.
Definition: Defines.h:73
void SetStance(const double a_Stance)
Definition: Player.h:131
void Kick(const AString &a_Reason)
AString m_IncomingData
Queue for the incoming data received on the link until it is processed in Tick(). ...
Definition: ClientHandle.h:439
std::vector< AString > AStringVector
Definition: StringUtils.h:14
Utilities to allow casting a cWorld to one of its interfaces without including World.h.
Definition: OpaqueWorld.h:12
cCriticalSection m_CSOutgoingData
Protects m_OutgoingData against multithreaded access.
Definition: ClientHandle.h:442
void StreamChunk(int a_ChunkX, int a_ChunkZ, cChunkSender::eChunkPriority a_Priority)
Adds a single chunk to be streamed to the client; used by StreamChunks()
void SendPlayerListAddPlayer(const cPlayer &a_Player)
cStatManager & GetStatManager()
Return the associated statistic and achievement manager.
Definition: Player.h:225
bool AddChunkClient(int a_ChunkX, int a_ChunkZ, cClientHandle *a_Client)
Adds client to a chunk, if not already present; returns true if added, false if present.
Definition: World.cpp:2770
void HandleBlockDigFinished(int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, BLOCKTYPE a_OldBlock, NIBBLETYPE a_OldMeta)
Handles the DIG_FINISHED dig packet:
void Authenticate(const AString &a_Name, const cUUID &a_UUID, const Json::Value &a_Properties)
Authenticates the specified user, called by cAuthenticator.
bool IsGameModeAdventure(void) const
Returns true if the player is in Adventure mode, either explicitly, or by inheriting from current wor...
Definition: Player.cpp:1278
virtual void OnLinkCreated(cTCPLinkPtr a_Link) override
Called when the cTCPLink for the connection is created.
int GetUniqueID(void) const
Definition: ClientHandle.h:250
void Normalize(void)
Definition: Vector3.h:44
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
size_t GetMaxPlayers(void) const
Definition: Server.h:70
Container for a single chat message composed of multiple functional parts.
Definition: CompositeChat.h:31
void PlayerCreated()
Notifies the server that a player was created; the server uses this to adjust the number of players...
Definition: Server.cpp:148
void SendEntityStatus(const cEntity &a_Entity, char a_Status)
void SendScoreUpdate(const AString &a_Objective, const AString &a_Player, cObjective::Score a_Score, Byte a_Mode)
#define MAX_EXPLOSIONS_PER_TICK
Maximum number of explosions to send this tick, server will start dropping if exceeded.
void SendEntityRelMoveLook(const cEntity &a_Entity, char a_RelX, char a_RelY, char a_RelZ)
void AddFaceDirection(int &a_BlockX, int &a_BlockY, int &a_BlockZ, eBlockFace a_BlockFace, bool a_bInverse=false)
Definition: Defines.h:859
void SendUnloadChunk(int a_ChunkX, int a_ChunkZ)
void SendEquippedSlot()
Sends the equipped item slot to the client.
Definition: Inventory.cpp:303
cUUID m_UUID
Contains the UUID used by Mojang to identify the player&#39;s account.
Definition: ClientHandle.h:532
void SendPaintingSpawn(const cPainting &a_Painting)
Vector3d GetLookVector(void) const
Definition: Entity.cpp:2209
std::chrono::steady_clock::duration m_Ping
Duration of the last completed client ping.
Definition: ClientHandle.h:472
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:460
void Authenticate(int a_ClientID, const AString &a_UserName, const AString &a_ServerHash)
Queues a request for authenticating a user.
void SendPlayerListUpdateGameMode(const cPlayer &a_Player)
bool CheckBlockInteractionsRate(void)
Returns true if the rate block interactions is within a reasonable limit (bot protection) ...
double GetSpawnY(void) const
Definition: World.h:692
void SendUpdateSign(int a_BlockX, int a_BlockY, int a_BlockZ, const AString &a_Line1, const AString &a_Line2, const AString &a_Line3, const AString &a_Line4)
bool GetBlockTypeMeta(Vector3i a_BlockPos, BLOCKTYPE &a_BlockType, NIBBLETYPE &a_BlockMeta)
Retrieves the block type and meta at the specified coords.
Definition: World.cpp:1909
UInt32 m_ProtocolVersion
The version of the protocol that the client is talking, or 0 if unknown.
Definition: ClientHandle.h:550
virtual void BroadcastSoundParticleEffect(const EffectID a_EffectID, Vector3i a_SrcPos, int a_Data, const cClientHandle *a_Exclude=nullptr) override
virtual ~cClientHandle() override
bool IsFrozen()
Is the player frozen?
Definition: Player.cpp:1675
Definition: World.h:65
AString m_IPString
Definition: ClientHandle.h:421
void SendEntityHeadLook(const cEntity &a_Entity)
cPlayer * GetPlayer(void)
Definition: ClientHandle.h:75
const char * ClickActionToString(int a_ClickAction)
Definition: Defines.h:242
void Respawn(void)
Definition: Player.cpp:1204
bool IsMinecart(void) const
Definition: Entity.h:176
void SendTitleTimes(int a_FadeInTicks, int a_DisplayTicks, int a_FadeOutTicks)
void SendCollectEntity(const cEntity &a_Entity, const cPlayer &a_Player, int a_Count)
void SendSetSubTitle(const cCompositeChat &a_SubTitle)
void InvalidateCachedSentChunk()
void HandleUseItem(eHand a_Hand)
AString m_OutgoingData
Buffer for storing outgoing data from any thread; will get sent in Tick() (to prevent deadlocks)...
Definition: ClientHandle.h:446
Serializes one chunk&#39;s data to (possibly multiple) protocol versions.
void SendTimeUpdate(Int64 a_WorldAge, Int64 a_TimeOfDay, bool a_DoDaylightCycle)
char m_ItemCount
Definition: Item.h:210
void SetCrouch(bool a_IsCrouched)
Sets the crouch status, broadcasts to all visible players.
Definition: Player.cpp:867
virtual int GetTimeOfDay(void) const override
Definition: World.h:110
Vector3< double > Vector3d
Definition: Vector3.h:445
void SendUseBed(const cEntity &a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ)
void BroadcastChatJoin(const AString &a_Message)
Definition: Root.h:184
std::shared_ptr< cTCPLink > cTCPLinkPtr
Definition: Network.h:17
void ServerTick(float a_Dt)
Called while the client is being ticked from the cServer object.
bool CallHookPlayerRightClick(cPlayer &a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ)
Disconnect packet sent, awaiting connection closure.
Definition: ClientHandle.h:501
void LOGINFO(const char *a_Format, fmt::ArgList a_ArgList)
Definition: Logger.cpp:165
float GetPlayerRelativeBlockHardness(BLOCKTYPE a_Block)
Returns the relative block hardness for the block a_Block.
Definition: Player.cpp:3066
AString & Printf(AString &str, const char *format, fmt::ArgList args)
Output the formatted text into the string.
Definition: StringUtils.cpp:55
void SetHotbarSlot(int a_HotBarSlotNum, const cItem &a_Item)
Puts a_Item item in a_HotBarSlotNum slot number in hotbar slots.
Definition: Inventory.cpp:285
void SendResetTitle(void)
bool CallHookPlayerTossingItem(cPlayer &a_Player)
int DeltaExperience(int a_Xp_delta)
Definition: Player.cpp:511
bool CallHookPlayerSpawned(cPlayer &a_Player)
virtual bool OnPlayerPlace(cWorld &a_World, cPlayer &a_Player, const cItem &a_EquippedItem, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ)
Called when the player tries to place the item (right mouse button, IsPlaceable() == true)...
void SendHideTitle(void)
virtual Int64 GetWorldAge(void) const override
Definition: World.h:109
void SendThunderbolt(int a_BlockX, int a_BlockY, int a_BlockZ)
void SetViewDistance(int a_ViewDistance)
Sets the maximal view distance.
The client is being destroyed, don&#39;t queue any more packets / don&#39;t add to chunks.
Definition: ClientHandle.h:503
void Destroy(void)
void SendBlockChange(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
cWorld * GetWorld(void)
Definition: ChunkMap.h:387
cScoreboard & GetScoreBoard(void)
Returns the associated scoreboard instance.
Definition: World.h:883
void HandleEntitySprinting(UInt32 a_EntityID, bool a_IsSprinting)
virtual void SetProperty(short a_Property, short a_Value, cPlayer &a_Player) override
Updates a numerical property associated with the window.
#define ASSERT(x)
Definition: Globals.h:335
The client has been authenticated, will start streaming chunks in the next tick.
Definition: ClientHandle.h:497
void LOGWARNING(const char *a_Format, fmt::ArgList a_ArgList)
Definition: Logger.cpp:174
void SendEntityAnimation(const cEntity &a_Entity, char a_Animation)
void HandleChat(const AString &a_Message)
Called when the protocol detects a chat packet.
cCriticalSection m_CSState
Definition: ClientHandle.h:508
cTCPLinkPtr m_Link
The link that is used for network communication.
Definition: ClientHandle.h:554
void SendUpdateBlockEntity(cBlockEntity &a_BlockEntity)
#define LOGD(...)
Definition: LoggerSimple.h:40
std::atomic< eState > m_State
The current (networking) state of the client.
Definition: ClientHandle.h:515
virtual eDimension GetDimension(void) const override
Definition: World.h:151
void SendChatAboveActionBar(const AString &a_Message, eMessageType a_ChatPrefix, const AString &a_AdditionalData="")
void SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer &a_Serializer)
void Unlock(void)
size_t GetNumPlayers(void) const
Definition: Server.h:71
void HandleEntityLeaveBed(UInt32 a_EntityID)
void HandlePluginMessage(const AString &a_Channel, const AString &a_Message)
void NotifyNearbyWolves(cPawn *a_Opponent, bool a_IsPlayerInvolved)
Notify nearby wolves that the player or one of the player&#39;s wolves took damage or did damage to an en...
Definition: Player.cpp:1054
void CloseWindow(bool a_CanRefuse=true)
Closes the current window, resets current window to m_InventoryWindow.
Definition: Player.cpp:1370
static const char * Yellow
Definition: ChatColor.h:31
void SendEntityEquipment(const cEntity &a_Entity, short a_SlotNum, const cItem &a_Item)
UInt32 m_PingID
ID of the last ping request sent to the client.
Definition: ClientHandle.h:475
void SendRemoveEntityEffect(const cEntity &a_Entity, int a_EffectID)
bool CallHookPlayerShooting(cPlayer &a_Player)
void SendPlayerPosition(void)
void HandleSpectate(const cUUID &a_PlayerUUID)
void SendWindowOpen(const cWindow &a_Window)
eHand
Definition: Defines.h:206
void SendChat(const AString &a_Message, eMessageType a_ChatPrefix, const AString &a_AdditionalData="")
int m_ChunkZ
Definition: ChunkDef.h:60
cBeaconEntity * GetBeaconEntity(void) const
Definition: BeaconWindow.h:27
double GetSpawnZ(void) const
Definition: World.h:693
void UpdateMovementStats(const Vector3d &a_DeltaPos, bool a_PreviousIsOnGround)
Update movement-related statistics.
Definition: Player.cpp:2556
void SendDisconnect(const AString &a_Reason)
void BroadcastPlayerListsAddPlayer(const cPlayer &a_Player, const cClientHandle *a_Exclude=nullptr)
Broadcast playerlist addition through all worlds.
Definition: Root.cpp:806
#define UNUSED
Definition: Globals.h:152
Vector3d m_ConfirmPosition
Definition: ClientHandle.h:448
void SendEntityRelMove(const cEntity &a_Entity, char a_RelX, char a_RelY, char a_RelZ)
void SendPickupSpawn(const cPickup &a_Pickup)
int m_LastStreamedChunkZ
Definition: ClientHandle.h:466
T Clamp(T a_Value, T a_Min, T a_Max)
Clamp X to the specified range.
Definition: Globals.h:351
void HandleBlockDigStarted(int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, BLOCKTYPE a_OldBlock, NIBBLETYPE a_OldMeta)
Handles the DIG_STARTED dig packet:
Json::Value m_Properties
Definition: ClientHandle.h:425
eDimension m_LastSentDimension
The dimension that was last sent to a player in a Respawn or Login packet.
Definition: ClientHandle.h:400
cBlockHandler * BlockHandler(BLOCKTYPE a_BlockType)
Definition: BlockInfo.h:159
void HandleCommandBlockEntityChange(UInt32 a_EntityID, const AString &a_NewCommand)
Called when the protocol receives a MC|AdvCdm plugin message, indicating that the player set a new co...
virtual void OnRightClicked(cPlayer &a_Player)
Called when the specified player right-clicks this entity.
Definition: Entity.h:523
bool IsSatiated(void) const
Returns true if the player is satiated, i.
Definition: Player.h:328
cWorld * GetDefaultWorld(void)
Definition: Root.cpp:632
unsigned char UInt8
Definition: Globals.h:115
cChunkMap * GetChunkMap(void)
Definition: World.h:1044
void HandleLeftClick(int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, UInt8 a_Status)
void TakeDamage(cEntity &a_Attacker)
Makes this pawn take damage from an attack by a_Attacker.
Definition: Entity.cpp:269
int m_ChunkX
Definition: ChunkDef.h:59
void GenerateOfflineUUID(void)
Generates an UUID based on the username stored for this client, and stores it in the m_UUID member...
short GetPropertyValue(short a_Property)
Return the value of a property.
std::string AString
Definition: StringUtils.h:13
eBlockFace
Block face constants, used in PlayerDigging and PlayerBlockPlacement packets and bbox collision calc...
Definition: Defines.h:29
void BroadcastPlayerListsRemovePlayer(const cPlayer &a_Player, const cClientHandle *a_Exclude=nullptr)
Broadcast playerlist removal through all worlds.
Definition: Root.cpp:818
bool CallHookPlayerUsedBlock(cPlayer &a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
void SendHealth(void)
#define FLOGD(...)
Definition: LoggerSimple.h:48
bool IsPlayer(void) const
Definition: Entity.h:171
bool m_IsForgeClient
True if the client advertised itself as a Forge client.
virtual bool IsUseable(void)
Checks if the block can be placed at this point.
virtual bool IsPlaceable(void)
Blocks simply get placed.
cServer * GetServer(void)
Definition: Root.h:69
cCriticalSection m_CSIncomingData
Protects m_IncomingData against multithreaded access.
Definition: ClientHandle.h:435
std::unique_ptr< cProtocol > m_Protocol
Definition: ClientHandle.h:432
void SendGameMode(eGameMode a_GameMode)
#define MAX_BLOCK_CHANGE_INTERACTIONS
Maximum number of block change interactions a player can perform per tick - exceeding this causes a k...
static int XpForLevel(int a_Level)
Calculates the amount of XP needed for a given level Ref: https://minecraft.gamepedia.com/XP.
Definition: Player.cpp:447
bool SetCommandBlockCommand(int a_BlockX, int a_BlockY, int a_BlockZ, const AString &a_Command)
Sets the command block command.
Definition: World.cpp:2877
void SendPlayerMoveLook(void)
void SendStatistics(const cStatManager &a_Manager)
virtual void AttachTo(cEntity *a_AttachTo) override
Attaches to the specified entity; detaches from any previous one first.
Definition: Player.cpp:2858
bool WantsSendChunk(int a_ChunkX, int a_ChunkZ)
Returns true if the client wants the chunk specified to be sent (in m_ChunksToSend) ...
int GetChunkX(void) const
Definition: Entity.h:218
bool CheckMultiLogin(const AString &a_Username)
Kicks the client if the same username is already logged in.
void SendEntityEffect(const cEntity &a_Entity, int a_EffectID, int a_Amplifier, int a_Duration)
void AbortEating(void)
Aborts the current eating operation.
Definition: Player.cpp:726
bool IsGameModeSurvival(void) const
Returns true if the player is in Survival mode, either explicitly, or by inheriting from current worl...
Definition: Player.cpp:1269
bool CallHookPlayerUsingItem(cPlayer &a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, int a_CursorX, int a_CursorY, int a_CursorZ)
static cRoot * Get()
Definition: Root.h:51
void LOG(const char *a_Format, fmt::ArgList a_ArgList)
Definition: Logger.cpp:156
const AString & GetUsername(void) const
const cItem & GetShieldSlot() const
Returns current item in shield slot.
Definition: Inventory.cpp:377
void SendExperienceOrb(const cExpOrb &a_ExpOrb)
bool HandleLogin(const AString &a_Username)
Called when the protocol has finished logging the user in.
virtual bool IsDrinkable(short a_ItemDamage)
Indicates if this item is drinkable.
Vector3d GetEyePosition(void) const
Definition: Player.cpp:1251
The client has just connected, waiting for their handshake / login.
Definition: ClientHandle.h:495
void UseEquippedItem(short a_Damage=1)
Damage the player&#39;s equipped item by a_Damage, possibly less if the equipped item is enchanted...
Definition: Player.cpp:2397
void SendUnleashEntity(const cEntity &a_Entity)
std::unordered_set< cChunkCoords, cChunkCoordsHash > m_ChunksToSend
Definition: ClientHandle.h:429
void SendSpawnVehicle(const cEntity &a_Vehicle, char a_VehicleType, char a_VehicleSubType=0)
static const char * Green
Definition: ChatColor.h:19
Definition: Entity.h:73
static const char * Italic
Definition: ChatColor.h:40
RAII for cCriticalSection - locks the CS on creation, unlocks on destruction.
void SendTo(cClientHandle &a_Client)
Send this scoreboard to the specified client.
Definition: Scoreboard.cpp:563
void PacketBufferFull(void)
const Vector3d & GetPosition(void) const
Exported in ManualBindings.
Definition: Entity.h:307
float m_BreakProgress
Definition: ClientHandle.h:559
void SendSetRawTitle(const AString &a_Title)
void SendWindowClose(const cWindow &a_Window)
bool CallHookPlayerEating(cPlayer &a_Player)
unsigned int UInt32
Definition: Globals.h:113
bool DoWithPlayerByUUID(const cUUID &a_PlayerUUID, cPlayerListCallback a_Callback)
Finds the player over his uuid and calls the callback.
Definition: World.cpp:2622
void RemoveChunkClient(int a_ChunkX, int a_ChunkZ, cClientHandle *a_Client)
Removes client from the chunk specified.
Definition: World.cpp:2779
Represents a UI window.
Definition: Window.h:53
void ProcessProtocolInOut(void)
Processes the data in the network input and output buffers.
void SteerVehicle(float a_Forward, float a_Sideways)
Definition: Entity.cpp:2185
int m_NumExplosionsThisTick
Number of explosions sent this tick.
Definition: ClientHandle.h:521
virtual const cItem * GetSlot(int a_SlotNum, cPlayer &a_Player) const =0
Called to retrieve an item in the specified slot for the specified player.
cAuthenticator & GetAuthenticator(void)
Definition: Root.h:115
void SendPlayerAbilities(void)
virtual void OnCancelRightClick(cChunkInterface &a_ChunkInterface, cWorldInterface &a_WorldInterface, cPlayer &a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace)
Called when a right click to this block is cancelled.
Definition: BlockHandler.h:119
void HandleBeaconSelection(int a_PrimaryEffect, int a_SecondaryEffect)
Called when the protocol receives a MC|Beacon plugin message, indicating that the player set an effec...
std::list< cChunkCoords > cChunkCoordsList
Definition: ChunkDef.h:566
eMessageType
Definition: Defines.h:976
bool EnchantByXPLevels(int a_NumXPLevels)
Randomly enchants the item using the specified number of XP levels.
Definition: Item.cpp:354
int m_BlockDigAnimSpeed
Definition: ClientHandle.h:482
void SendChunkTo(int a_ChunkX, int a_ChunkZ, cChunkSender::eChunkPriority a_Priority, cClientHandle *a_Client)
Sends the chunk to the client specified, if the client doesn&#39;t have the chunk yet.
Definition: World.cpp:2797
void SendPlayerListUpdatePing(const cPlayer &a_Player)
void TabCompleteUserName(const AString &a_Text, AStringVector &a_Results)
Appends all usernames starting with a_Text (case-insensitive) into Results.
Definition: World.cpp:3280
double GetPosZ(void) const
Definition: Entity.h:208
AString ItemToFullString(const cItem &a_Item)
Translates a full item into a fully-specified string (including meta and count).
Definition: BlockID.cpp:283
unsigned char Byte
Definition: Globals.h:117
bool IsGameModeCreative(void) const
Returns true if the player is in Creative mode, either explicitly, or by inheriting from current worl...
Definition: Player.cpp:1260
void HandleEntityCrouch(UInt32 a_EntityID, bool a_IsCrouching)
char GetWindowID(void) const
Definition: Window.h:80
static int s_ClientCount
Definition: ClientHandle.h:526
static bool IsOneHitDig(BLOCKTYPE a_Type)
Definition: BlockInfo.h:33
short m_ItemType
Definition: Item.h:209
void RegisterPluginChannels(const AStringVector &a_ChannelList)
Adds all of the channels to the list of current plugin channels.
virtual void SetSlot(int a_SlotNum, cPlayer &a_Player, const cItem &a_Item)=0
Called to set an item in the specified slot for the specified player.
bool SetSecondaryEffect(cEntityEffect::eType a_Effect)
Sets the secondary effect.
void SetYaw(double a_Yaw)
Definition: Entity.cpp:2059
void HandleUseEntity(UInt32 a_TargetEntityID, bool a_IsLeftClick)
bool IsGameModeSpectator(void) const
Returns true if the player is in Spectator mode, either explicitly, or by inheriting from current wor...
Definition: Player.cpp:1287
void HandleSlotSelected(Int16 a_SlotNum)
const cItem * GetSlot(cPlayer &a_Player, int a_SlotNum) const
Returns the item at the specified slot for the specified player.
Definition: Window.cpp:106
int m_CurrentViewDistance
The actual view distance used, the minimum of client&#39;s requested view distance and world&#39;s max view d...
Definition: ClientHandle.h:416
void FinishAuthenticate(const AString &a_Name, const cUUID &a_UUID, const Json::Value &a_Properties)
Finish logging the user in after authenticating.
static const char * White
Definition: ChatColor.h:32
bool SetPrimaryEffect(cEntityEffect::eType a_Effect)
Sets the primary effect.
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:362
AStringVector GetPlayerTabCompletionMultiWorld(const AString &a_Text)
Returns the completions for a player name across all worlds.
Definition: Root.cpp:1130
cInventory & GetInventory(void)
Definition: Player.h:136
bool m_HasSentDC
True if a Disconnect packet has been sent in either direction.
Definition: ClientHandle.h:462
This class represents the player&#39;s inventory The slots are divided into three areas: ...
Definition: Inventory.h:32
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:123
void DestroyNoScheduling(bool a_ShouldBroadcast)
Destroy the entity without scheduling memory freeing.
Definition: Entity.cpp:254
The client will be destroyed in the next tick (flag set when socket closed)
Definition: ClientHandle.h:502
Definition: Item.h:36
void SendPluginMessage(const AString &a_Channel, const AString &a_Message)
void HandleAnvilItemName(const AString &a_ItemName)
Called when the protocol receives a MC|ItemName plugin message, indicating that the player named an i...
bool IsNil() const
Returns true if this contains the "nil" UUID with all bits set to 0.
Definition: UUID.h:30
void AddWantedChunk(int a_ChunkX, int a_ChunkZ)
Adds the chunk specified to the list of chunks wanted for sending (m_ChunksToSend) ...
EffectID
Definition: EffectID.h:5
virtual bool IsFood(void)
Indicates if this item is food.
void SetUsername(const AString &a_Username)
void SendEntityMetadata(const cEntity &a_Entity)
bool CallHookPlayerBrokenBlock(cPlayer &a_Player, int a_BlockX, int a_BlockY, int a_BlockZ, char a_BlockFace, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
cItemHandler * ItemHandler(int a_ItemType)
Definition: ItemHandler.h:174
void AddFoodExhaustion(double a_Exhaustion)
Adds the specified exhaustion to m_FoodExhaustion.
Definition: Player.cpp:672
signed long long Int64
Definition: Globals.h:107
void SendPlayerListUpdateDisplayName(const cPlayer &a_Player, const AString &a_CustomName)
cEntityEffect::eType GetPrimaryEffect(void) const
Definition: BeaconEntity.h:50
void BeginForgeHandshake(const AString &a_Name, const cUUID &a_UUID, const Json::Value &a_Properties)
Begin the Forge Modloader Handshake (FML|HS) sequence.
std::vector< sSetBlock > sSetBlockVector
Definition: ChunkDef.h:564
std::shared_ptr< cClientHandle > cClientHandlePtr
Definition: ClientHandle.h:39
void SendDestroyEntity(const cEntity &a_Entity)
UInt32 GetUniqueID(void) const
Definition: Entity.h:261
virtual void SendBlockTo(int a_X, int a_Y, int a_Z, cPlayer &a_Player) override
Sends the block on those coords to the player.
Definition: World.cpp:2224
int m_UniqueID
ID used for identification during authenticating.
Definition: ClientHandle.h:529
cWorld * GetWorld(void) const
Definition: Entity.h:201
bool AreCommandBlocksEnabled(void) const
Definition: World.h:888
T Diff(T a_Val1, T a_Val2)
Definition: Defines.h:965
cPluginManager * GetPluginManager(void)
Definition: Root.h:114
void BroadcastWholeWindow(void)
Sends the contents of the whole window to all clients of this window.
Definition: Window.cpp:738
void SetEquippedSlotNum(int a_SlotNum)
Sets equiped item to the a_SlotNum slot number.
Definition: Inventory.cpp:395
const AString & GetIPString(void) const
Definition: ClientHandle.h:69
cSlotArea * m_SlotArea
static const std::chrono::milliseconds PING_TIME_MS
The interval for sending pings to clients.
double GetSpawnX(void) const
Definition: World.h:691
void HandleUpdateSign(int a_BlockX, int a_BlockY, int a_BlockZ, const AString &a_Line1, const AString &a_Line2, const AString &a_Line3, const AString &a_Line4)
int m_RequestedViewDistance
The requested view distance from the player.
Definition: ClientHandle.h:419
virtual bool Initialize(OwnedEntity a_Self, cWorld &a_World) override
Spawns the entity in the world; returns true if spawned, false if not (plugin disallowed).
Definition: Player.cpp:187
static const char * LightBlue
Definition: ChatColor.h:28