diff options
Diffstat (limited to '')
-rw-r--r-- | src/ClientHandle.h | 185 |
1 files changed, 97 insertions, 88 deletions
diff --git a/src/ClientHandle.h b/src/ClientHandle.h index c49de647f..78caa9532 100644 --- a/src/ClientHandle.h +++ b/src/ClientHandle.h @@ -21,13 +21,15 @@ #include "json/json.h" #include "ChunkSender.h" #include "EffectID.h" - +#include "Protocol/ProtocolRecognizer.h" #include <array> #include <atomic> +#include <unordered_map> // fwd: +class cChunk; class cChunkDataSerializer; class cInventory; class cMonster; @@ -36,7 +38,6 @@ class cExpOrb; class cPainting; class cPickup; class cPlayer; -class cProtocol; class cWindow; class cFallingBlock; class cItemHandler; @@ -44,14 +45,14 @@ class cWorld; class cCompositeChat; class cStatManager; class cClientHandle; -typedef SharedPtr<cClientHandle> cClientHandlePtr; -class cClientHandle // tolua_export - : public cTCPLink::cCallbacks +class cClientHandle : // tolua_export + public std::enable_shared_from_this<cClientHandle>, + public cTCPLink::cCallbacks { // tolua_export public: // tolua_export @@ -63,6 +64,17 @@ public: // tolua_export static const int MAX_VIEW_DISTANCE = 32; static const int MIN_VIEW_DISTANCE = 1; + enum class eState + { + csConnected, ///< The client has just connected, waiting for their handshake / login + csAuthenticating, ///< The client has logged in, waiting for external authentication + csAuthenticated, ///< The client has been authenticated, will start streaming chunks in the next tick + csConfirmingPos, ///< The client has been sent the position packet, waiting for them to repeat the position back + csPlaying, ///< Normal gameplay + csDestroying, ///< The client is being destroyed, don't queue any more packets / don't add to chunks + csDestroyed, ///< The client has been destroyed, the destructor is to be called from the owner thread + }; + /** Creates a new client with the specified IP address in its description and the specified initial view distance. */ cClientHandle(const AString & a_IPString, int a_ViewDistance); @@ -86,6 +98,11 @@ public: // tolua_export const Json::Value & GetProperties(void) const { return m_Properties; } + /** Atomically sets the internal client handle state. + Allows only a consecutive state to be set. + Returns if the state was set or not. */ + bool SetState(eState a_State); + /** Sets the player's properties, such as skin image and signature. Used mainly by BungeeCord compatibility code - property querying is done on the BungeeCord server and the results are passed to MCS running in offline mode. */ @@ -119,43 +136,48 @@ public: // tolua_export /** Authenticates the specified user, called by cAuthenticator */ void Authenticate(const AString & a_Name, const AString & a_UUID, const Json::Value & a_Properties); - /** This function sends a new unloaded chunk to the player. Returns true if all chunks are loaded. */ - bool StreamNextChunk(); + /** Send all necessary chunks to the player */ + void StreamAllChunks(); /** Remove all loaded chunks that are no longer in range */ void UnloadOutOfRangeChunks(void); - // Removes the client from all chunks. Used when switching worlds or destroying the player - void RemoveFromAllChunks(void); - - inline bool IsLoggedIn(void) const { return (m_State >= csAuthenticating); } + inline bool IsLoggedIn(void) const { return (m_State >= eState::csAuthenticating); } /** Called while the client is being ticked from the world via its cPlayer object */ void Tick(float a_Dt); - /** Called while the client is being ticked from the cServer object */ - void ServerTick(float a_Dt); - + /** Mark the clienthandle as disconnected. + Guarantees that no more data is sent or received. Therefore, MUST be called in context of server tick thread. + Will result in cServer releasing the shared_ptr to the handle. + Will eventually result in the object's destruction. */ void Destroy(void); - bool IsPlaying (void) const { return (m_State == csPlaying); } - bool IsDestroyed (void) const { return (m_State == csDestroyed); } - bool IsDestroying(void) const { return (m_State == csDestroying); } + bool IsPlaying (void) const { return (m_State == eState::csPlaying); } + bool IsDestroyed (void) const { return (m_State == eState::csDestroyed); } + bool IsDestroying(void) const { return (m_State == eState::csDestroying); } // The following functions send the various packets: // (Please keep these alpha-sorted) void SendAttachEntity (const cEntity & a_Entity, const cEntity & a_Vehicle); void SendBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType); void SendBlockBreakAnim (UInt32 a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage); - void SendBlockChange (int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); // tolua_export - void SendBlockChanges (int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes); + + /** Sends a singular block change notification to the client. + Callers MUST ensure that the client has been sent or is going to be sent the chunk specified by the arguments. */ + void SendBlockChange(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta); + + /** Sends multiple block changes in a single notification to the client. + Callers MUST ensure that the client has been sent or is going to be sent the chunk specified by the arguments. */ + void SendBlockChanges(int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes); + void SendChat (const AString & a_Message, eMessageType a_ChatPrefix, const AString & a_AdditionalData = ""); void SendChat (const cCompositeChat & a_Message); void SendChatAboveActionBar (const AString & a_Message, eMessageType a_ChatPrefix, const AString & a_AdditionalData = ""); void SendChatAboveActionBar (const cCompositeChat & a_Message); void SendChatSystem (const AString & a_Message, eMessageType a_ChatPrefix, const AString & a_AdditionalData = ""); void SendChatSystem (const cCompositeChat & a_Message); - void SendChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer); + void SendChunkData (cWorld & a_ChunkSenderWorld, const cChunkCoords & a_ChunkCoordinates, cChunkDataSerializer & a_Serializer); void SendCollectEntity (const cEntity & a_Entity, const cPlayer & a_Player); void SendDestroyEntity (const cEntity & a_Entity); void SendDetachEntity (const cEntity & a_Entity, const cEntity & a_PreviousVehicle); @@ -217,7 +239,6 @@ public: // tolua_export void SendThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ); void SendTitleTimes (int a_FadeInTicks, int a_DisplayTicks, int a_FadeOutTicks); void SendTimeUpdate (Int64 a_WorldAge, Int64 a_TimeOfDay, bool a_DoDaylightCycle); // tolua_export - void SendUnloadChunk (int a_ChunkX, int a_ChunkZ); void SendUpdateBlockEntity (cBlockEntity & a_BlockEntity); 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); void SendUseBed (const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ); @@ -258,18 +279,12 @@ public: // tolua_export // tolua_end - /** Returns true if the client wants the chunk specified to be sent (in m_ChunksToSend) */ - bool WantsSendChunk(int a_ChunkX, int a_ChunkZ); - - /** Adds the chunk specified to the list of chunks wanted for sending (m_ChunksToSend) */ - void AddWantedChunk(int a_ChunkX, int a_ChunkZ); - // Calls that cProtocol descendants use to report state: void PacketBufferFull(void); void PacketUnknown(UInt32 a_PacketType); void PacketError(UInt32 a_PacketType); - // Calls that cProtocol descendants use for handling packets: + /** Calls that cProtocol descendants use for handling packets. */ void HandleAnimation(int a_Animation); /** Called when the protocol receives a MC|ItemName plugin message, indicating that the player named @@ -319,10 +334,9 @@ public: // tolua_export the NPC UI. */ void HandleNPCTrade(int a_SlotNum); - void HandlePing (void); void HandlePlayerAbilities (bool a_CanFly, bool a_IsFlying, float FlyingSpeed, float WalkingSpeed); void HandlePlayerLook (float a_Rotation, float a_Pitch, bool a_IsOnGround); - void HandlePlayerMoveLook (double a_PosX, double a_PosY, double a_PosZ, double a_Stance, float a_Rotation, float a_Pitch, bool a_IsOnGround); // While m_bPositionConfirmed (normal gameplay) + void HandlePlayerMoveLook (double a_PosX, double a_PosY, double a_PosZ, double a_Stance, float a_Rotation, float a_Pitch, bool a_IsOnGround); /** Verifies and sets player position, performing relevant checks Calls relevant methods to process movement related statistics @@ -361,12 +375,25 @@ public: // tolua_export /** Called by the protocol recognizer when the protocol version is known. */ void SetProtocolVersion(UInt32 a_ProtocolVersion) { m_ProtocolVersion = a_ProtocolVersion; } + /** Called by cServer::Shutdown to indicate m_Player's destruction is out of our control */ + void StopPlayerDestructionManagement() { m_ShouldDestroyPlayer = false; } + /** Returns the protocol version number of the protocol that the client is talking. Returns zero if the protocol version is not (yet) known. */ UInt32 GetProtocolVersion(void) const { return m_ProtocolVersion; } // tolua_export - void InvalidateCachedSentChunk(); + /** Add a new functor which will commit processed incoming network data in the world tick thread */ + template <typename FunctionType, typename... FunctionArguments> + void QueueDataCommit(FunctionType && a_Function, FunctionArguments && ... a_FunctionArguments) + { + m_DataCommitQueue.emplace_back(std::bind(std::forward<FunctionType>(a_Function), std::forward<FunctionArguments>(a_FunctionArguments)...)); + } + + /** Enforces a rate limit on block interactions. + Will kick client if limit exceeded. + Returns true if the block interactions rate is within MAX_BLOCK_CHANGE_INTERACTIONS per tick. */ + bool EnforceBlockInteractionsRate(void); - bool IsPlayerChunkSent(); + cProtocol & GetProtocol(void) { return m_Protocol; } private: @@ -385,15 +412,35 @@ private: AString m_IPString; AString m_Username; - AString m_Password; Json::Value m_Properties; - cCriticalSection m_CSChunkLists; - std::unordered_set<cChunkCoords, cChunkCoordsHash> m_LoadedChunks; // Chunks that the player belongs to - std::unordered_set<cChunkCoords, cChunkCoordsHash> m_ChunksToSend; // Chunks that need to be sent to the player (queued because they weren't generated yet or there's not enough time to send them) - cChunkCoordsList m_SentChunks; // Chunks that are currently sent to the client + /** Flag indicating whether it is safe to access m_Player in our destructor to destroy it + While used inter-thread, atomicity not necessary as is only ever set in the main thread and read in a destructor, which is not called as long as someone has a reference to us */ + bool m_ShouldDestroyPlayer; + + struct sChunkCoordsHash + { + size_t operator()(const cChunkCoords & a_ChunkCoords) const + { + size_t Seed = static_cast<size_t>(a_ChunkCoords.m_ChunkX) + 0x9e3779b9; + return (Seed ^= static_cast<size_t>(a_ChunkCoords.m_ChunkZ) + 0x9e3779b9 + (Seed << 6) + (Seed >> 2)); + } + }; + + /** Chunks that are currently loaded by the player */ + std::unordered_map<cChunkCoords, std::reference_wrapper<cWorld>, cChunkCoordsHash> m_LoadedChunks; - cProtocol * m_Protocol; + /** Functors which will commit processed incoming network data to the world. + Functors MUST be called in the context of the world's tick thread */ + std::vector<std::function<void()>> m_DataCommitQueue; + + cProtocolRecognizer m_Protocol; + + /** Empties the incoming send queue in a thread safe manner and processes the data. */ + void ProcessQueuedIncomingData(void); + + /** Empties the data commit queue and queues the data to be executed in the context of the world tick thread. */ + void ProcessDataCommitQueue(void); /** Protects m_IncomingData against multithreaded access. */ cCriticalSection m_CSIncomingData; @@ -402,30 +449,23 @@ private: Protected by m_CSIncomingData. */ AString m_IncomingData; + /** Empties the outgoing send queue in a thread safe manner and sends the data via the network link. */ + void ProcessQueuedOutgoingData(void); + /** Protects m_OutgoingData against multithreaded access. */ cCriticalSection m_CSOutgoingData; - /** Buffer for storing outgoing data from any thread; will get sent in Tick() (to prevent deadlocks). + /** Queue for the outgoing data to be sent through the link until it is processed in Tick(). Protected by m_CSOutgoingData. */ AString m_OutgoingData; - Vector3d m_ConfirmPosition; + /** Protects m_Link against multithreaded access. */ + cCriticalSection m_CSLink; + /** Non-owning pointer to the player object we are associated with + It is guaranteed to exist whilst the server is running and this clienthandle is alive */ cPlayer * m_Player; - /** This is an optimization which saves you an iteration of m_SentChunks if you just want to know - whether or not the player is standing at a sent chunk. - If this is equal to the coordinates of the chunk the player is currrently standing at, then this must be a sent chunk - and a member of m_SentChunks. - Otherwise, this contains an arbitrary value which should not be used. */ - cChunkCoords m_CachedSentChunk; - - bool m_HasSentDC; ///< True if a Disconnect packet has been sent in either direction - - // Chunk position when the last StreamChunks() was called; used to avoid re-streaming while in the same chunk - int m_LastStreamedChunkX; - int m_LastStreamedChunkZ; - /** Number of ticks since the last network packet was received (increased in Tick(), reset in OnReceivedData()) */ std::atomic<int> m_TicksSinceLastPacket; @@ -451,27 +491,11 @@ private: int m_LastDigBlockY; int m_LastDigBlockZ; - enum eState - { - csConnected, ///< The client has just connected, waiting for their handshake / login - csAuthenticating, ///< The client has logged in, waiting for external authentication - csAuthenticated, ///< The client has been authenticated, will start streaming chunks in the next tick - csDownloadingWorld, ///< The client is waiting for chunks, we're waiting for the loader to provide and send them - csConfirmingPos, ///< The client has been sent the position packet, waiting for them to repeat the position back - csPlaying, ///< Normal gameplay - csDestroying, ///< The client is being destroyed, don't queue any more packets / don't add to chunks - csDestroyed, ///< The client has been destroyed, the destructor is to be called from the owner thread - - // TODO: Add Kicking here as well - } ; - std::atomic<eState> m_State; - /** m_State needs to be locked in the Destroy() function so that the destruction code doesn't run twice on two different threads */ - cCriticalSection m_CSDestroyingState; - - /** If set to true during csDownloadingWorld, the tick thread calls CheckIfWorldDownloaded() */ - bool m_ShouldCheckDownloaded; + /** A flag to indicate whether a player has traversed chunks and should therefore have their view resent. + The flag is set in position updates, and causes StreamAllChunks to be called in the next server tick. */ + bool m_ShouldRefreshSentChunks; /** Number of explosions sent this tick */ int m_NumExplosionsThisTick; @@ -487,9 +511,6 @@ private: /** Contains the UUID used by Mojang to identify the player's account. Short UUID stored here (without dashes) */ AString m_UUID; - /** Set to true when the chunk where the player is is sent to the client. Used for spawning the player */ - bool m_HasSentPlayerChunk; - /** Client Settings */ AString m_Locale; @@ -509,16 +530,7 @@ private: m_CSOutgoingData is used to synchronize access for sending data. */ cTCPLinkPtr m_Link; - /** Shared pointer to self, so that this instance can keep itself alive when needed. */ - cClientHandlePtr m_Self; - - - /** Returns true if the rate block interactions is within a reasonable limit (bot protection) */ - bool CheckBlockInteractionsRate(void); - - /** Adds a single chunk to be streamed to the client; used by StreamChunks() */ - void StreamChunk(int a_ChunkX, int a_ChunkZ, cChunkSender::eChunkPriority a_Priority); - + /** Handles the DIG_STARTED dig packet: */ void HandleBlockDigStarted (int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace, BLOCKTYPE a_OldBlock, NIBBLETYPE a_OldMeta); @@ -540,9 +552,6 @@ private: /** Called when the network socket has been closed. */ void SocketClosed(void); - /** Called right after the instance is created to store its SharedPtr inside. */ - void SetSelf(cClientHandlePtr a_Self); - // cTCPLink::cCallbacks overrides: virtual void OnLinkCreated(cTCPLinkPtr a_Link) override; virtual void OnReceivedData(const char * a_Data, size_t a_Length) override; |