2012-06-14 09:06:06 -04:00
# include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
2012-09-23 18:09:57 -04:00
# include "ClientHandle.h"
# include "Server.h"
# include "World.h"
2013-08-19 05:39:13 -04:00
# include "Entities/Pickup.h"
2013-12-08 06:17:54 -05:00
# include "Bindings/PluginManager.h"
2013-08-19 05:39:13 -04:00
# include "Entities/Player.h"
2012-09-23 18:09:57 -04:00
# include "Inventory.h"
2013-05-28 15:12:47 -04:00
# include "BlockEntities/ChestEntity.h"
2014-01-18 12:58:46 -05:00
# include "BlockEntities/CommandBlockEntity.h"
2013-05-28 15:12:47 -04:00
# include "BlockEntities/SignEntity.h"
2012-09-23 13:19:34 -04:00
# include "UI/Window.h"
2012-09-23 18:09:57 -04:00
# include "Item.h"
2012-09-23 16:53:08 -04:00
# include "Mobs/Monster.h"
2012-09-23 18:09:57 -04:00
# include "ChatColor.h"
2012-09-23 17:23:33 -04:00
# include "OSSupport/Socket.h"
# include "OSSupport/Timer.h"
2012-09-29 09:59:32 -04:00
# include "Items/ItemHandler.h"
# include "Blocks/BlockHandler.h"
2013-08-25 10:11:19 -04:00
# include "Blocks/BlockSlab.h"
2014-01-26 09:20:39 -05:00
# include "Blocks/ChunkInterface.h"
2012-06-14 09:06:06 -04:00
2012-09-23 18:09:57 -04:00
# include "Root.h"
2012-06-14 09:06:06 -04:00
2014-04-13 07:04:56 -04:00
# include "Protocol/Authenticator.h"
2012-06-14 09:06:06 -04:00
# include "MersenneTwister.h"
2012-09-23 16:03:26 -04:00
# include "Protocol/ProtocolRecognizer.h"
2014-02-15 17:16:44 -05:00
# include "CompositeChat.h"
2014-02-23 14:02:44 -05:00
# include "Items/ItemSword.h"
2012-06-14 09:06:06 -04:00
2014-04-14 14:21:00 -04:00
# include "md5/md5.h"
2012-06-14 09:06:06 -04:00
2014-02-04 18:40:58 -05:00
/** Maximum number of explosions to send this tick, server will start dropping if exceeded */
2014-02-04 19:45:08 -05:00
# define MAX_EXPLOSIONS_PER_TICK 20
2013-06-18 15:32:31 -04:00
2014-03-01 16:24:36 -05:00
/** Maximum number of block change interactions a player can perform per tick - exceeding this causes a kick */
# define MAX_BLOCK_CHANGE_INTERACTIONS 20
2014-02-04 18:40:58 -05:00
/** How many ticks before the socket is closed after the client is destroyed (#31) */
2013-08-20 16:27:29 -04:00
static const int TICKS_BEFORE_CLOSE = 20 ;
2012-06-14 09:06:06 -04:00
# define RECI_RAND_MAX (1.f / RAND_MAX)
inline int fRadRand ( MTRand & r1 , int a_BlockCoord )
{
return a_BlockCoord * 32 + ( int ) ( 16 * ( ( float ) r1 . rand ( ) * RECI_RAND_MAX ) * 16 - 8 ) ;
}
int cClientHandle : : s_ClientCount = 0 ;
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cClientHandle:
2013-12-20 13:10:07 -05:00
cClientHandle : : cClientHandle ( const cSocket * a_Socket , int a_ViewDistance ) :
m_ViewDistance ( a_ViewDistance ) ,
m_IPString ( a_Socket - > GetIPString ( ) ) ,
m_OutgoingData ( 64 KiB ) ,
m_Player ( NULL ) ,
m_HasSentDC ( false ) ,
m_LastStreamedChunkX ( 0x7fffffff ) , // bogus chunk coords to force streaming upon login
m_LastStreamedChunkZ ( 0x7fffffff ) ,
m_TimeSinceLastPacket ( 0 ) ,
m_Ping ( 1000 ) ,
m_PingID ( 1 ) ,
m_BlockDigAnimStage ( - 1 ) ,
m_HasStartedDigging ( false ) ,
m_TicksSinceDestruction ( 0 ) ,
m_State ( csConnected ) ,
m_ShouldCheckDownloaded ( false ) ,
2014-02-04 18:40:58 -05:00
m_NumExplosionsThisTick ( 0 ) ,
2013-12-20 13:10:07 -05:00
m_UniqueID ( 0 ) ,
2014-03-08 11:33:38 -05:00
m_HasSentPlayerChunk ( false ) ,
m_Locale ( " en_GB " )
2012-06-14 09:06:06 -04:00
{
2012-08-30 05:56:59 -04:00
m_Protocol = new cProtocolRecognizer ( this ) ;
2012-08-27 13:31:16 -04:00
2012-06-14 09:06:06 -04:00
s_ClientCount + + ; // Not protected by CS because clients are always constructed from the same thread
m_UniqueID = s_ClientCount ;
cTimer t1 ;
m_LastPingTime = t1 . GetNowTime ( ) ;
2012-08-27 13:31:16 -04:00
LOGD ( " New ClientHandle created at %p " , this ) ;
2012-06-14 09:06:06 -04:00
}
cClientHandle : : ~ cClientHandle ( )
{
2013-08-20 16:27:29 -04:00
ASSERT ( m_State > = csDestroyedWaiting ) ; // Has Destroy() been called?
2012-11-11 09:00:58 -05:00
2012-08-27 13:31:16 -04:00
LOGD ( " Deleting client \" %s \" at %p " , GetUsername ( ) . c_str ( ) , this ) ;
2012-06-14 09:06:06 -04:00
{
cCSLock Lock ( m_CSChunkLists ) ;
m_LoadedChunks . clear ( ) ;
m_ChunksToSend . clear ( ) ;
}
if ( m_Player ! = NULL )
{
cWorld * World = m_Player - > GetWorld ( ) ;
if ( ! m_Username . empty ( ) & & ( World ! = NULL ) )
{
// Send the Offline PlayerList packet:
2012-08-27 13:31:16 -04:00
World - > BroadcastPlayerListItem ( * m_Player , false , this ) ;
2012-06-14 09:06:06 -04:00
}
if ( World ! = NULL )
{
World - > RemovePlayer ( m_Player ) ;
2013-04-13 17:02:10 -04:00
m_Player - > Destroy ( ) ;
2012-06-14 09:06:06 -04:00
}
2013-04-13 17:02:10 -04:00
delete m_Player ;
m_Player = NULL ;
2012-06-14 09:06:06 -04:00
}
2013-01-05 14:05:15 -05:00
if ( ! m_HasSentDC )
2012-06-14 09:06:06 -04:00
{
2012-09-25 04:23:19 -04:00
SendDisconnect ( " Server shut down? Kthnxbai " ) ;
2012-06-14 09:06:06 -04:00
}
2014-01-19 13:31:43 -05:00
// Close the socket as soon as it sends all outgoing data:
2012-09-25 04:23:19 -04:00
cRoot : : Get ( ) - > GetServer ( ) - > RemoveClient ( this ) ;
2012-06-14 09:06:06 -04:00
2012-08-27 13:31:16 -04:00
delete m_Protocol ;
m_Protocol = NULL ;
LOGD ( " ClientHandle at %p deleted " , this ) ;
2012-06-14 09:06:06 -04:00
}
2013-08-13 16:45:29 -04:00
void cClientHandle : : Destroy ( void )
2012-06-14 09:06:06 -04:00
{
2013-08-13 16:45:29 -04:00
{
cCSLock Lock ( m_CSDestroyingState ) ;
if ( m_State > = csDestroying )
{
// Already called
return ;
}
m_State = csDestroying ;
}
// DEBUG:
LOGD ( " %s: client %p, \" %s \" " , __FUNCTION__ , this , m_Username . c_str ( ) ) ;
2012-06-14 09:06:06 -04:00
if ( ( m_Player ! = NULL ) & & ( m_Player - > GetWorld ( ) ! = NULL ) )
{
RemoveFromAllChunks ( ) ;
m_Player - > GetWorld ( ) - > RemoveClientFromChunkSender ( this ) ;
}
2013-08-20 16:27:29 -04:00
m_State = csDestroyedWaiting ;
2012-06-14 09:06:06 -04:00
}
2014-04-14 14:21:00 -04:00
void cClientHandle : : GenerateOfflineUUID ( void )
2014-04-14 16:52:59 -04:00
{
m_UUID = GenerateOfflineUUID ( m_Username ) ;
}
2014-04-26 12:21:49 -04:00
AString cClientHandle : : FormatChatPrefix ( bool ShouldAppendChatPrefixes , AString a_ChatPrefixS , AString m_Color1 , AString m_Color2 )
{
if ( ShouldAppendChatPrefixes )
2014-04-26 17:01:48 -04:00
return Printf ( " %s[%s] %s " , m_Color1 . c_str ( ) , a_ChatPrefixS . c_str ( ) , m_Color2 . c_str ( ) ) ;
2014-04-26 12:21:49 -04:00
else
return Printf ( " %s " , m_Color1 . c_str ( ) ) ;
}
2014-04-24 20:24:39 -04:00
AString cClientHandle : : FormatMessageType ( bool ShouldAppendChatPrefixes , eMessageType a_ChatPrefix , const AString & a_AdditionalData )
{
switch ( a_ChatPrefix )
{
2014-04-27 12:35:41 -04:00
case mtCustom : return " " ;
2014-04-26 17:01:48 -04:00
case mtFailure : return FormatChatPrefix ( ShouldAppendChatPrefixes , " INFO " , cChatColor : : Rose , cChatColor : : White ) ;
case mtInformation : return FormatChatPrefix ( ShouldAppendChatPrefixes , " INFO " , cChatColor : : Yellow , cChatColor : : White ) ;
case mtSuccess : return FormatChatPrefix ( ShouldAppendChatPrefixes , " INFO " , cChatColor : : Green , cChatColor : : White ) ;
case mtWarning : return FormatChatPrefix ( ShouldAppendChatPrefixes , " WARN " , cChatColor : : Rose , cChatColor : : White ) ;
case mtFatal : return FormatChatPrefix ( ShouldAppendChatPrefixes , " FATAL " , cChatColor : : Red , cChatColor : : White ) ;
case mtDeath : return FormatChatPrefix ( ShouldAppendChatPrefixes , " DEATH " , cChatColor : : Gray , cChatColor : : White ) ;
case mtJoin : return FormatChatPrefix ( ShouldAppendChatPrefixes , " JOIN " , cChatColor : : Yellow , cChatColor : : White ) ;
case mtLeave : return FormatChatPrefix ( ShouldAppendChatPrefixes , " LEAVE " , cChatColor : : Yellow , cChatColor : : White ) ;
2014-04-24 20:24:39 -04:00
case mtPrivateMessage :
{
if ( ShouldAppendChatPrefixes )
2014-04-28 14:37:22 -04:00
{
2014-04-24 20:24:39 -04:00
return Printf ( " %s[MSG: %s] %s%s " , cChatColor : : LightBlue . c_str ( ) , a_AdditionalData . c_str ( ) , cChatColor : : White . c_str ( ) , cChatColor : : Italic . c_str ( ) ) ;
2014-04-28 14:37:22 -04:00
}
2014-04-24 20:24:39 -04:00
else
2014-04-28 14:37:22 -04:00
{
2014-04-24 20:24:39 -04:00
return Printf ( " %s: %s " , a_AdditionalData . c_str ( ) , cChatColor : : LightBlue . c_str ( ) ) ;
2014-04-28 14:37:22 -04:00
}
2014-04-24 20:24:39 -04:00
}
}
ASSERT ( ! " Unhandled chat prefix type! " ) ;
2014-04-27 12:35:41 -04:00
return " " ;
2014-04-24 20:24:39 -04:00
}
2014-04-14 16:52:59 -04:00
AString cClientHandle : : GenerateOfflineUUID ( const AString & a_Username )
2014-04-14 14:21:00 -04:00
{
// Proper format for a version 3 UUID is:
// xxxxxxxx-xxxx-3xxx-yxxx-xxxxxxxxxxxx where x is any hexadecimal digit and y is one of 8, 9, A, or B
// Generate an md5 checksum, and use it as base for the ID:
2014-04-14 16:52:59 -04:00
MD5 Checksum ( a_Username ) ;
AString UUID = Checksum . hexdigest ( ) ;
UUID [ 12 ] = ' 3 ' ; // Version 3 UUID
UUID [ 16 ] = ' 8 ' ; // Variant 1 UUID
2014-04-14 14:21:00 -04:00
// Now the digest doesn't have the UUID slashes, but the client requires them, so add them into the appropriate positions:
2014-04-14 16:52:59 -04:00
UUID . insert ( 8 , " - " ) ;
UUID . insert ( 13 , " - " ) ;
UUID . insert ( 18 , " - " ) ;
UUID . insert ( 23 , " - " ) ;
return UUID ;
2014-04-14 14:21:00 -04:00
}
2012-06-14 09:06:06 -04:00
void cClientHandle : : Kick ( const AString & a_Reason )
{
if ( m_State > = csAuthenticating ) // Don't log pings
{
2013-12-07 09:41:58 -05:00
LOGINFO ( " Kicking player %s for \" %s \" " , m_Username . c_str ( ) , StripColorCodes ( a_Reason ) . c_str ( ) ) ;
2012-06-14 09:06:06 -04:00
}
2012-08-25 17:59:13 -04:00
SendDisconnect ( a_Reason ) ;
2012-06-14 09:06:06 -04:00
}
2014-04-13 07:04:56 -04:00
void cClientHandle : : Authenticate ( const AString & a_Name , const AString & a_UUID )
2012-06-14 09:06:06 -04:00
{
if ( m_State ! = csAuthenticating )
{
return ;
}
ASSERT ( m_Player = = NULL ) ;
2014-04-13 07:04:56 -04:00
m_Username = a_Name ;
m_UUID = a_UUID ;
2014-04-14 16:52:59 -04:00
// Send login success (if the protocol supports it):
m_Protocol - > SendLoginSuccess ( ) ;
2014-04-13 07:04:56 -04:00
2012-06-14 09:06:06 -04:00
// Spawn player (only serversided, so data is loaded)
m_Player = new cPlayer ( this , GetUsername ( ) ) ;
cWorld * World = cRoot : : Get ( ) - > GetWorld ( m_Player - > GetLoadedWorldName ( ) ) ;
if ( World = = NULL )
{
World = cRoot : : Get ( ) - > GetDefaultWorld ( ) ;
}
2012-08-27 13:31:16 -04:00
if ( m_Player - > GetGameMode ( ) = = eGameMode_NotSet )
{
2012-07-13 11:26:27 -04:00
m_Player - > LoginSetGameMode ( World - > GetGameMode ( ) ) ;
2012-08-27 13:31:16 -04:00
}
2012-06-14 09:06:06 -04:00
2012-09-25 04:23:19 -04:00
m_Player - > SetIP ( m_IPString ) ;
2012-06-14 09:06:06 -04:00
2014-02-03 16:12:44 -05:00
if ( ! cRoot : : Get ( ) - > GetPluginManager ( ) - > CallHookPlayerJoined ( * m_Player ) )
{
2014-02-05 18:24:16 -05:00
cRoot : : Get ( ) - > BroadcastChatJoin ( Printf ( " %s has joined the game " , GetUsername ( ) . c_str ( ) ) ) ;
2014-02-03 16:12:44 -05:00
LOGINFO ( " Player %s has joined the game. " , m_Username . c_str ( ) ) ;
}
2012-09-23 12:54:03 -04:00
m_ConfirmPosition = m_Player - > GetPosition ( ) ;
2012-06-14 09:06:06 -04:00
// Return a server login packet
2012-09-02 08:09:58 -04:00
m_Protocol - > SendLogin ( * m_Player , * World ) ;
2012-06-14 09:06:06 -04:00
// Send Weather if raining:
if ( ( World - > GetWeather ( ) = = 1 ) | | ( World - > GetWeather ( ) = = 2 ) )
{
2012-08-27 13:31:16 -04:00
m_Protocol - > SendWeather ( World - > GetWeather ( ) ) ;
2012-06-14 09:06:06 -04:00
}
// Send time
2012-11-01 17:38:20 -04:00
m_Protocol - > SendTimeUpdate ( World - > GetWorldAge ( ) , World - > GetTimeOfDay ( ) ) ;
2012-06-14 09:06:06 -04:00
2013-11-05 12:36:58 -05:00
// Send contents of the inventory window
m_Protocol - > SendWholeInventory ( * m_Player - > GetWindow ( ) ) ;
2012-06-14 09:06:06 -04:00
// Send health
2012-07-17 08:02:03 -04:00
m_Player - > SendHealth ( ) ;
2013-11-15 10:23:50 -05:00
// Send experience
m_Player - > SendExperience ( ) ;
2012-06-14 09:06:06 -04:00
2014-06-08 15:58:08 -04:00
m_Player - > Initialize ( * World ) ;
2013-08-13 16:45:29 -04:00
m_State = csAuthenticated ;
2014-01-21 08:58:17 -05:00
// Query player team
m_Player - > UpdateTeam ( ) ;
2014-01-21 12:43:13 -05:00
// Send scoreboard data
World - > GetScoreBoard ( ) . SendTo ( * this ) ;
2014-05-11 07:57:06 -04:00
2014-04-29 17:10:50 -04:00
// Delay the first ping until the client "settles down"
// This should fix #889, "BadCast exception, cannot convert bit to fm" error in client
cTimer t1 ;
m_LastPingTime = t1 . GetNowTime ( ) + 3000 ; // Send the first KeepAlive packet in 3 seconds
2014-01-21 12:43:13 -05:00
2013-01-11 23:46:01 -05:00
cRoot : : Get ( ) - > GetPluginManager ( ) - > CallHookPlayerSpawned ( * m_Player ) ;
2012-06-14 09:06:06 -04:00
}
void cClientHandle : : StreamChunks ( void )
{
2013-08-14 04:24:34 -04:00
if ( ( m_State < csAuthenticated ) | | ( m_State > = csDestroying ) )
2012-06-14 09:06:06 -04:00
{
return ;
}
ASSERT ( m_Player ! = NULL ) ;
2014-06-08 15:58:08 -04:00
2013-02-27 05:01:20 -05:00
int ChunkPosX = FAST_FLOOR_DIV ( ( int ) m_Player - > GetPosX ( ) , cChunkDef : : Width ) ;
int ChunkPosZ = FAST_FLOOR_DIV ( ( int ) m_Player - > GetPosZ ( ) , cChunkDef : : Width ) ;
2012-06-14 09:06:06 -04:00
if ( ( ChunkPosX = = m_LastStreamedChunkX ) & & ( ChunkPosZ = = m_LastStreamedChunkZ ) )
{
// Already streamed for this position
return ;
}
m_LastStreamedChunkX = ChunkPosX ;
m_LastStreamedChunkZ = ChunkPosZ ;
LOGD ( " Streaming chunks centered on [%d, %d], view distance %d " , ChunkPosX , ChunkPosZ , m_ViewDistance ) ;
cWorld * World = m_Player - > GetWorld ( ) ;
ASSERT ( World ! = NULL ) ;
// Remove all loaded chunks that are no longer in range; deferred to out-of-CS:
cChunkCoordsList RemoveChunks ;
{
cCSLock Lock ( m_CSChunkLists ) ;
for ( cChunkCoordsList : : iterator itr = m_LoadedChunks . begin ( ) ; itr ! = m_LoadedChunks . end ( ) ; )
{
int RelX = ( * itr ) . m_ChunkX - ChunkPosX ;
int RelZ = ( * itr ) . m_ChunkZ - ChunkPosZ ;
if ( ( RelX > m_ViewDistance ) | | ( RelX < - m_ViewDistance ) | | ( RelZ > m_ViewDistance ) | | ( RelZ < - m_ViewDistance ) )
{
RemoveChunks . push_back ( * itr ) ;
itr = m_LoadedChunks . erase ( itr ) ;
}
else
{
+ + itr ;
}
} // for itr - m_LoadedChunks[]
for ( cChunkCoordsList : : iterator itr = m_ChunksToSend . begin ( ) ; itr ! = m_ChunksToSend . end ( ) ; )
{
int RelX = ( * itr ) . m_ChunkX - ChunkPosX ;
int RelZ = ( * itr ) . m_ChunkZ - ChunkPosZ ;
if ( ( RelX > m_ViewDistance ) | | ( RelX < - m_ViewDistance ) | | ( RelZ > m_ViewDistance ) | | ( RelZ < - m_ViewDistance ) )
{
itr = m_ChunksToSend . erase ( itr ) ;
}
else
{
+ + itr ;
}
} // for itr - m_ChunksToSend[]
}
for ( cChunkCoordsList : : iterator itr = RemoveChunks . begin ( ) ; itr ! = RemoveChunks . end ( ) ; + + itr )
{
2013-04-13 17:02:10 -04:00
World - > RemoveChunkClient ( itr - > m_ChunkX , itr - > m_ChunkZ , this ) ;
2012-08-27 13:31:16 -04:00
m_Protocol - > SendUnloadChunk ( itr - > m_ChunkX , itr - > m_ChunkZ ) ;
2012-06-14 09:06:06 -04:00
} // for itr - RemoveChunks[]
// Add all chunks that are in range and not yet in m_LoadedChunks:
// Queue these smartly - from the center out to the edge
for ( int d = 0 ; d < = m_ViewDistance ; + + d ) // cycle through (square) distance, from nearest to furthest
{
// For each distance add chunks in a hollow square centered around current position:
for ( int i = - d ; i < = d ; + + i )
{
2013-04-13 17:02:10 -04:00
StreamChunk ( ChunkPosX + d , ChunkPosZ + i ) ;
StreamChunk ( ChunkPosX - d , ChunkPosZ + i ) ;
2012-06-14 09:06:06 -04:00
} // for i
for ( int i = - d + 1 ; i < d ; + + i )
{
2013-04-13 17:02:10 -04:00
StreamChunk ( ChunkPosX + i , ChunkPosZ + d ) ;
StreamChunk ( ChunkPosX + i , ChunkPosZ - d ) ;
2012-06-14 09:06:06 -04:00
} // for i
} // for d
// Touch chunks GENERATEDISTANCE ahead to let them generate:
for ( int d = m_ViewDistance + 1 ; d < = m_ViewDistance + GENERATEDISTANCE ; + + d ) // cycle through (square) distance, from nearest to furthest
{
// For each distance touch chunks in a hollow square centered around current position:
for ( int i = - d ; i < = d ; + + i )
{
World - > TouchChunk ( ChunkPosX + d , ZERO_CHUNK_Y , ChunkPosZ + i ) ;
World - > TouchChunk ( ChunkPosX - d , ZERO_CHUNK_Y , ChunkPosZ + i ) ;
} // for i
for ( int i = - d + 1 ; i < d ; + + i )
{
World - > TouchChunk ( ChunkPosX + i , ZERO_CHUNK_Y , ChunkPosZ + d ) ;
World - > TouchChunk ( ChunkPosX + i , ZERO_CHUNK_Y , ChunkPosZ - d ) ;
} // for i
} // for d
}
2013-04-13 17:02:10 -04:00
void cClientHandle : : StreamChunk ( int a_ChunkX , int a_ChunkZ )
2012-06-14 09:06:06 -04:00
{
2012-11-11 09:00:58 -05:00
if ( m_State > = csDestroying )
{
// Don't stream chunks to clients that are being destroyed
return ;
}
2012-06-14 09:06:06 -04:00
cWorld * World = m_Player - > GetWorld ( ) ;
ASSERT ( World ! = NULL ) ;
2013-04-13 17:02:10 -04:00
if ( World - > AddChunkClient ( a_ChunkX , a_ChunkZ , this ) )
2012-06-14 09:06:06 -04:00
{
{
cCSLock Lock ( m_CSChunkLists ) ;
2013-04-13 17:02:10 -04:00
m_LoadedChunks . push_back ( cChunkCoords ( a_ChunkX , ZERO_CHUNK_Y , a_ChunkZ ) ) ;
m_ChunksToSend . push_back ( cChunkCoords ( a_ChunkX , ZERO_CHUNK_Y , a_ChunkZ ) ) ;
2012-06-14 09:06:06 -04:00
}
2013-04-13 17:02:10 -04:00
World - > SendChunkTo ( a_ChunkX , a_ChunkZ , this ) ;
2012-06-14 09:06:06 -04:00
}
}
// Removes the client from all chunks. Used when switching worlds or destroying the player
void cClientHandle : : RemoveFromAllChunks ( )
{
cWorld * World = m_Player - > GetWorld ( ) ;
if ( World ! = NULL )
{
World - > RemoveClientFromChunks ( this ) ;
}
{
cCSLock Lock ( m_CSChunkLists ) ;
m_LoadedChunks . clear ( ) ;
m_ChunksToSend . clear ( ) ;
2013-07-03 03:47:35 -04:00
// Also reset the LastStreamedChunk coords to bogus coords,
// so that all chunks are streamed in subsequent StreamChunks() call (FS #407)
m_LastStreamedChunkX = 0x7fffffff ;
m_LastStreamedChunkZ = 0x7fffffff ;
2012-06-14 09:06:06 -04:00
}
}
void cClientHandle : : HandlePing ( void )
{
// Somebody tries to retrieve information about the server
AString Reply ;
2014-04-19 11:53:02 -04:00
const cServer & Server = * cRoot : : Get ( ) - > GetServer ( ) ;
2014-04-18 15:09:44 -04:00
2012-06-14 09:06:06 -04:00
Printf ( Reply , " %s%s%i%s%i " ,
2014-04-19 11:53:02 -04:00
Server . GetDescription ( ) . c_str ( ) ,
2013-08-11 13:18:06 -04:00
cChatColor : : Delimiter . c_str ( ) ,
2014-04-19 11:53:02 -04:00
Server . GetNumPlayers ( ) ,
2013-08-11 13:18:06 -04:00
cChatColor : : Delimiter . c_str ( ) ,
2014-04-19 11:53:02 -04:00
Server . GetMaxPlayers ( )
2012-06-14 09:06:06 -04:00
) ;
2014-04-18 15:09:44 -04:00
Kick ( Reply ) ;
2012-06-14 09:06:06 -04:00
}
2012-08-30 04:19:19 -04:00
bool cClientHandle : : HandleLogin ( int a_ProtocolVersion , const AString & a_Username )
2012-06-14 09:06:06 -04:00
{
2012-08-18 05:56:28 -04:00
LOGD ( " LOGIN %s " , a_Username . c_str ( ) ) ;
2012-08-30 04:19:19 -04:00
m_Username = a_Username ;
2012-06-14 09:06:06 -04:00
2012-08-18 05:56:28 -04:00
if ( cRoot : : Get ( ) - > GetPluginManager ( ) - > CallHookLogin ( this , a_ProtocolVersion , a_Username ) )
2012-06-14 09:06:06 -04:00
{
Destroy ( ) ;
2012-08-30 04:19:19 -04:00
return false ;
2012-06-14 09:06:06 -04:00
}
// Schedule for authentication; until then, let them wait (but do not block)
m_State = csAuthenticating ;
2012-09-06 13:36:59 -04:00
cRoot : : Get ( ) - > GetAuthenticator ( ) . Authenticate ( GetUniqueID ( ) , GetUsername ( ) , m_Protocol - > GetAuthServerID ( ) ) ;
2012-08-30 04:19:19 -04:00
return true ;
2012-06-14 09:06:06 -04:00
}
2012-08-18 05:56:28 -04:00
void cClientHandle : : HandleCreativeInventory ( short a_SlotNum , const cItem & a_HeldItem )
2012-06-14 09:06:06 -04:00
{
// This is for creative Inventory changes
2013-10-29 16:19:06 -04:00
if ( ! m_Player - > IsGameModeCreative ( ) )
2012-06-14 09:06:06 -04:00
{
2012-09-20 09:25:54 -04:00
LOGWARNING ( " Got a CreativeInventoryAction packet from user \" %s \" while not in creative mode. Ignoring. " , m_Username . c_str ( ) ) ;
return ;
2012-06-14 09:06:06 -04:00
}
2013-10-28 08:30:24 -04:00
if ( m_Player - > GetWindow ( ) - > GetWindowType ( ) ! = cWindow : : wtInventory )
2012-06-14 09:06:06 -04:00
{
2012-09-20 09:25:54 -04:00
LOGWARNING ( " Got a CreativeInventoryAction packet from user \" %s \" while not in the inventory window. Ignoring. " , m_Username . c_str ( ) ) ;
return ;
2012-06-14 09:06:06 -04:00
}
2012-09-20 09:25:54 -04:00
2013-05-08 05:45:07 -04:00
m_Player - > GetWindow ( ) - > Clicked ( * m_Player , 0 , a_SlotNum , ( a_SlotNum > = 0 ) ? caLeftClick : caLeftClickOutside , a_HeldItem ) ;
2012-06-14 09:06:06 -04:00
}
2013-12-15 09:11:59 -05:00
void cClientHandle : : HandlePlayerAbilities ( bool a_CanFly , bool a_IsFlying , float FlyingSpeed , float WalkingSpeed )
2013-12-15 08:48:17 -05:00
{
2013-12-19 15:53:47 -05:00
UNUSED ( FlyingSpeed ) ; // Ignore the client values for these
UNUSED ( WalkingSpeed ) ;
2013-12-15 09:11:59 -05:00
m_Player - > SetCanFly ( a_CanFly ) ;
m_Player - > SetFlying ( a_IsFlying ) ;
2013-12-15 08:48:17 -05:00
}
2012-08-18 05:56:28 -04:00
void cClientHandle : : HandlePlayerPos ( double a_PosX , double a_PosY , double a_PosZ , double a_Stance , bool a_IsOnGround )
2012-06-14 09:06:06 -04:00
{
2013-09-08 12:36:06 -04:00
if ( ( m_Player = = NULL ) | | ( m_State ! = csPlaying ) )
{
// The client hasn't been spawned yet and sends nonsense, we know better
return ;
}
2014-05-26 10:38:14 -04:00
/*
// TODO: Invalid stance check
if ( ( a_PosY > = a_Stance ) | | ( a_Stance > a_PosY + 1.65 ) )
2012-08-19 15:42:32 -04:00
{
2014-05-26 10:38:14 -04:00
LOGD ( " Invalid stance " ) ;
SendPlayerMoveLook ( ) ;
2014-05-16 16:04:19 -04:00
return ;
}
2014-05-26 10:38:14 -04:00
*/
2013-09-08 12:36:06 -04:00
// If the player has moved too far, "repair" them:
2012-08-19 15:42:32 -04:00
Vector3d Pos ( a_PosX , a_PosY , a_PosZ ) ;
if ( ( m_Player - > GetPosition ( ) - Pos ) . SqrLength ( ) > 100 * 100 )
{
LOGD ( " Too far away (%0.2f), \" repairing \" the client " , ( m_Player - > GetPosition ( ) - Pos ) . Length ( ) ) ;
SendPlayerMoveLook ( ) ;
return ;
}
2013-07-12 16:01:25 -04:00
// If a jump just started, process food exhaustion:
if ( ( a_PosY > m_Player - > GetPosY ( ) ) & & ! a_IsOnGround & & m_Player - > IsOnGround ( ) )
{
2013-08-08 11:57:34 -04:00
// we only add this exhaustion if the player is not swimming - otherwise we end up with both jump + swim exhaustion
2013-08-09 03:37:39 -04:00
2013-09-08 12:36:06 -04:00
if ( ! m_Player - > IsSwimming ( ) )
2013-08-08 11:57:34 -04:00
{
m_Player - > AddFoodExhaustion ( m_Player - > IsSprinting ( ) ? 0.8 : 0.2 ) ;
}
2013-07-12 16:01:25 -04:00
}
2012-08-19 15:42:32 -04:00
m_Player - > MoveTo ( Pos ) ;
2012-08-18 05:56:28 -04:00
m_Player - > SetStance ( a_Stance ) ;
m_Player - > SetTouchGround ( a_IsOnGround ) ;
2012-06-14 09:06:06 -04:00
}
2014-01-07 11:47:05 -05:00
void cClientHandle : : HandlePluginMessage ( const AString & a_Channel , const AString & a_Message )
{
2014-02-20 17:24:39 -05:00
if ( a_Channel = = " MC|AdvCdm " )
2014-01-18 12:58:46 -05:00
{
2014-02-20 17:24:39 -05:00
// Command block, set text, Client -> Server
HandleCommandBlockMessage ( a_Message . c_str ( ) , a_Message . size ( ) ) ;
2014-01-18 20:06:19 -05:00
}
2014-02-20 17:24:39 -05:00
else if ( a_Channel = = " MC|Brand " )
2014-01-18 20:06:19 -05:00
{
2014-02-20 17:24:39 -05:00
// Client <-> Server branding exchange
2014-01-18 20:06:19 -05:00
SendPluginMessage ( " MC|Brand " , " MCServer " ) ;
2014-02-20 17:24:39 -05:00
}
2014-04-30 19:25:04 -04:00
else if ( a_Channel = = " MC|ItemName " )
{
HandleAnvilItemName ( a_Message . c_str ( ) , a_Message . size ( ) ) ;
}
2014-02-20 17:24:39 -05:00
else if ( a_Channel = = " REGISTER " )
{
2014-03-06 15:06:53 -05:00
if ( HasPluginChannel ( a_Channel ) )
{
SendPluginMessage ( " UNREGISTER " , a_Channel ) ;
return ; // Can't register again if already taken - kinda defeats the point of plugin messaging!
}
2014-02-20 17:24:39 -05:00
RegisterPluginChannels ( BreakApartPluginChannels ( a_Message ) ) ;
}
else if ( a_Channel = = " UNREGISTER " )
{
UnregisterPluginChannels ( BreakApartPluginChannels ( a_Message ) ) ;
2014-01-18 12:58:46 -05:00
}
2014-03-06 15:06:53 -05:00
else if ( ! HasPluginChannel ( a_Channel ) )
{
// Ignore if client sent something but didn't register the channel first
LOGD ( " Player %s sent a plugin message on channel \" %s \" , but didn't REGISTER it first " , GetUsername ( ) . c_str ( ) , a_Channel . c_str ( ) ) ;
SendPluginMessage ( " UNREGISTER " , a_Channel ) ;
return ;
}
2014-01-18 12:58:46 -05:00
2014-01-07 11:47:05 -05:00
cPluginManager : : Get ( ) - > CallHookPluginMessage ( * this , a_Channel , a_Message ) ;
}
2014-02-20 17:24:39 -05:00
AStringVector cClientHandle : : BreakApartPluginChannels ( const AString & a_PluginChannels )
{
// Break the string on each NUL character.
// Note that StringSplit() doesn't work on this because NUL is a special char - string terminator
size_t len = a_PluginChannels . size ( ) ;
size_t first = 0 ;
AStringVector res ;
for ( size_t i = 0 ; i < len ; i + + )
{
if ( a_PluginChannels [ i ] ! = 0 )
{
continue ;
}
if ( i > first )
{
res . push_back ( a_PluginChannels . substr ( first , i - first ) ) ;
}
first = i + 1 ;
} // for i - a_PluginChannels[]
if ( first < len )
{
res . push_back ( a_PluginChannels . substr ( first , len - first ) ) ;
}
return res ;
}
void cClientHandle : : RegisterPluginChannels ( const AStringVector & a_ChannelList )
{
for ( AStringVector : : const_iterator itr = a_ChannelList . begin ( ) , end = a_ChannelList . end ( ) ; itr ! = end ; + + itr )
{
m_PluginChannels . insert ( * itr ) ;
} // for itr - a_ChannelList[]
}
void cClientHandle : : UnregisterPluginChannels ( const AStringVector & a_ChannelList )
{
for ( AStringVector : : const_iterator itr = a_ChannelList . begin ( ) , end = a_ChannelList . end ( ) ; itr ! = end ; + + itr )
{
m_PluginChannels . erase ( * itr ) ;
} // for itr - a_ChannelList[]
}
2014-05-01 17:03:23 -04:00
void cClientHandle : : HandleCommandBlockMessage ( const char * a_Data , size_t a_Length )
2014-01-18 12:58:46 -05:00
{
if ( a_Length < 14 )
{
2014-02-07 13:58:52 -05:00
SendChat ( " Failure setting command block command; bad request " , mtFailure ) ;
2014-01-18 12:58:46 -05:00
LOGD ( " Malformed MC|AdvCdm packet. " ) ;
return ;
}
2014-01-18 14:27:54 -05:00
cByteBuffer Buffer ( a_Length ) ;
Buffer . Write ( a_Data , a_Length ) ;
2014-01-18 12:58:46 -05:00
int BlockX , BlockY , BlockZ ;
AString Command ;
2014-01-18 14:27:54 -05:00
char Mode ;
Buffer . ReadChar ( Mode ) ;
switch ( Mode )
2014-01-18 12:58:46 -05:00
{
case 0x00 :
{
2014-01-18 14:27:54 -05:00
Buffer . ReadBEInt ( BlockX ) ;
Buffer . ReadBEInt ( BlockY ) ;
Buffer . ReadBEInt ( BlockZ ) ;
2014-01-18 12:58:46 -05:00
2014-01-18 14:27:54 -05:00
Buffer . ReadVarUTF8String ( Command ) ;
2014-01-18 12:58:46 -05:00
break ;
}
default :
{
2014-02-07 13:58:52 -05:00
SendChat ( " Failure setting command block command; unhandled mode " , mtFailure ) ;
2014-01-18 12:58:46 -05:00
LOGD ( " Unhandled MC|AdvCdm packet mode. " ) ;
return ;
}
}
cWorld * World = m_Player - > GetWorld ( ) ;
2014-01-23 07:57:04 -05:00
if ( World - > AreCommandBlocksEnabled ( ) )
{
World - > SetCommandBlockCommand ( BlockX , BlockY , BlockZ , Command ) ;
2014-02-05 18:24:16 -05:00
2014-02-07 13:58:52 -05:00
SendChat ( " Successfully set command block command " , mtSuccess ) ;
2014-01-23 07:57:04 -05:00
}
else
{
2014-02-07 13:58:52 -05:00
SendChat ( " Command blocks are not enabled on this server " , mtFailure ) ;
2014-01-23 07:57:04 -05:00
}
2014-01-18 12:58:46 -05:00
}
2014-05-05 09:42:41 -04:00
void cClientHandle : : HandleAnvilItemName ( const char * a_Data , size_t a_Length )
2014-04-30 19:25:04 -04:00
{
if ( a_Length < 1 )
{
return ;
}
if ( ( m_Player - > GetWindow ( ) = = NULL ) | | ( m_Player - > GetWindow ( ) - > GetWindowType ( ) ! = cWindow : : wtAnvil ) )
{
return ;
}
2014-05-06 11:31:02 -04:00
AString Name ( a_Data , a_Length ) ;
2014-04-30 19:25:04 -04:00
if ( Name . length ( ) < = 30 )
{
2014-05-06 11:31:02 -04:00
( ( cAnvilWindow * ) m_Player - > GetWindow ( ) ) - > SetRepairedItemName ( Name , m_Player ) ;
2014-04-30 19:25:04 -04:00
}
}
2014-02-04 13:59:05 -05:00
void cClientHandle : : HandleLeftClick ( int a_BlockX , int a_BlockY , int a_BlockZ , eBlockFace a_BlockFace , char a_Status )
2012-06-14 09:06:06 -04:00
{
2013-01-11 23:46:01 -05:00
LOGD ( " HandleLeftClick: {%i, %i, %i}; Face: %i; Stat: %i " ,
a_BlockX , a_BlockY , a_BlockZ , a_BlockFace , a_Status
) ;
2014-03-01 16:24:36 -05:00
m_NumBlockChangeInteractionsThisTick + + ;
if ( ! CheckBlockInteractionsRate ( ) )
{
Kick ( " Too many blocks were destroyed per unit time - hacked client? " ) ;
return ;
}
2014-05-15 13:58:48 -04:00
if (
2014-05-18 17:41:42 -04:00
( ( a_Status = = DIG_STATUS_STARTED ) | | ( a_Status = = DIG_STATUS_FINISHED ) ) & & // Only do a radius check for block destruction - things like pickup tossing send coordinates that are to be ignored
( ( Diff ( m_Player - > GetPosX ( ) , ( double ) a_BlockX ) > 6 ) | |
2014-05-15 13:58:48 -04:00
( Diff ( m_Player - > GetPosY ( ) , ( double ) a_BlockY ) > 6 ) | |
2014-05-18 17:41:42 -04:00
( Diff ( m_Player - > GetPosZ ( ) , ( double ) a_BlockZ ) > 6 ) )
2014-05-15 13:58:48 -04:00
)
{
m_Player - > GetWorld ( ) - > SendBlockTo ( a_BlockX , a_BlockY , a_BlockZ , m_Player ) ;
return ;
}
2013-01-11 23:46:01 -05:00
cPluginManager * PlgMgr = cRoot : : Get ( ) - > GetPluginManager ( ) ;
if ( PlgMgr - > CallHookPlayerLeftClick ( * m_Player , a_BlockX , a_BlockY , a_BlockZ , a_BlockFace , a_Status ) )
{
// A plugin doesn't agree with the action, replace the block on the client and quit:
m_Player - > GetWorld ( ) - > SendBlockTo ( a_BlockX , a_BlockY , a_BlockZ , m_Player ) ;
return ;
}
2012-06-14 09:06:06 -04:00
2013-01-11 23:46:01 -05:00
switch ( a_Status )
{
case DIG_STATUS_DROP_HELD : // Drop held item
{
if ( PlgMgr - > CallHookPlayerTossingItem ( * m_Player ) )
{
// A plugin doesn't agree with the tossing. The plugin itself is responsible for handling the consequences (possible inventory mismatch)
return ;
}
2014-01-23 02:27:39 -05:00
m_Player - > TossEquippedItem ( ) ;
2013-01-11 23:46:01 -05:00
return ;
}
case DIG_STATUS_SHOOT_EAT :
{
cItemHandler * ItemHandler = cItemHandler : : GetItemHandler ( m_Player - > GetEquippedItem ( ) ) ;
2014-06-07 19:32:37 -04:00
if ( ItemHandler - > IsFood ( ) | | ItemHandler - > IsDrinkable ( & m_Player - > GetEquippedItem ( ) ) )
2013-01-11 23:46:01 -05:00
{
2013-07-28 13:15:03 -04:00
m_Player - > AbortEating ( ) ;
return ;
2013-01-11 23:46:01 -05:00
}
else
{
if ( PlgMgr - > CallHookPlayerShooting ( * m_Player ) )
{
// A plugin doesn't agree with the action. The plugin itself is responsible for handling the consequences (possible inventory mismatch)
return ;
}
2013-08-30 08:24:03 -04:00
ItemHandler - > OnItemShoot ( m_Player , a_BlockX , a_BlockY , a_BlockZ , a_BlockFace ) ;
2013-01-11 23:46:01 -05:00
}
return ;
}
case DIG_STATUS_STARTED :
{
BLOCKTYPE OldBlock ;
NIBBLETYPE OldMeta ;
m_Player - > GetWorld ( ) - > GetBlockTypeMeta ( a_BlockX , a_BlockY , a_BlockZ , OldBlock , OldMeta ) ;
HandleBlockDigStarted ( a_BlockX , a_BlockY , a_BlockZ , a_BlockFace , OldBlock , OldMeta ) ;
return ;
}
case DIG_STATUS_FINISHED :
{
BLOCKTYPE OldBlock ;
NIBBLETYPE OldMeta ;
m_Player - > GetWorld ( ) - > GetBlockTypeMeta ( a_BlockX , a_BlockY , a_BlockZ , OldBlock , OldMeta ) ;
HandleBlockDigFinished ( a_BlockX , a_BlockY , a_BlockZ , a_BlockFace , OldBlock , OldMeta ) ;
return ;
}
2013-01-13 07:59:48 -05:00
case DIG_STATUS_CANCELLED :
{
// Block breaking cancelled by player
2014-05-11 05:56:15 -04:00
FinishDigAnimation ( ) ;
2013-01-13 07:59:48 -05:00
return ;
}
2014-01-15 17:36:19 -05:00
case DIG_STATUS_DROP_STACK :
{
if ( PlgMgr - > CallHookPlayerTossingItem ( * m_Player ) )
{
// A plugin doesn't agree with the tossing. The plugin itself is responsible for handling the consequences (possible inventory mismatch)
return ;
}
2014-01-23 02:27:39 -05:00
m_Player - > TossEquippedItem ( 64 ) ; // Toss entire slot - if there aren't enough items, the maximum will be ejected
2014-01-15 17:36:19 -05:00
return ;
}
2013-01-11 23:46:01 -05:00
default :
{
ASSERT ( ! " Unhandled DIG_STATUS " ) ;
return ;
}
} // switch (a_Status)
}
2012-06-14 09:06:06 -04:00
2013-01-11 23:46:01 -05:00
2014-02-04 13:59:05 -05:00
void cClientHandle : : HandleBlockDigStarted ( int a_BlockX , int a_BlockY , int a_BlockZ , eBlockFace a_BlockFace , BLOCKTYPE a_OldBlock , NIBBLETYPE a_OldMeta )
2013-01-11 23:46:01 -05:00
{
if (
m_HasStartedDigging & &
( a_BlockX = = m_LastDigBlockX ) & &
( a_BlockY = = m_LastDigBlockY ) & &
( a_BlockZ = = m_LastDigBlockZ )
)
2012-06-14 09:06:06 -04:00
{
2013-01-11 23:46:01 -05:00
// It is a duplicate packet, drop it right away
2012-06-14 09:06:06 -04:00
return ;
}
2014-05-09 17:10:02 -04:00
2014-02-23 14:02:44 -05:00
if (
m_Player - > IsGameModeCreative ( ) & &
2014-02-23 14:31:58 -05:00
ItemCategory : : IsSword ( m_Player - > GetInventory ( ) . GetEquippedItem ( ) . m_ItemType )
2014-02-23 14:02:44 -05:00
)
{
// Players can't destroy blocks with a Sword in the hand.
return ;
}
2014-05-09 17:10:02 -04:00
2014-05-15 13:58:48 -04:00
if (
( Diff ( m_Player - > GetPosX ( ) , ( double ) a_BlockX ) > 6 ) | |
( Diff ( m_Player - > GetPosY ( ) , ( double ) a_BlockY ) > 6 ) | |
( Diff ( m_Player - > GetPosZ ( ) , ( double ) a_BlockZ ) > 6 )
)
2012-07-15 16:36:34 -04:00
{
2013-01-11 23:46:01 -05:00
m_Player - > GetWorld ( ) - > SendBlockTo ( a_BlockX , a_BlockY , a_BlockZ , m_Player ) ;
2012-08-18 05:56:28 -04:00
return ;
2012-07-15 16:36:34 -04:00
}
2014-05-15 13:58:48 -04:00
2013-01-11 23:46:01 -05:00
// 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:
m_HasStartedDigging = true ;
m_LastDigBlockX = a_BlockX ;
m_LastDigBlockY = a_BlockY ;
m_LastDigBlockZ = a_BlockZ ;
2012-06-14 09:06:06 -04:00
2013-02-14 16:47:57 -05:00
if (
2013-10-29 16:45:31 -04:00
( m_Player - > IsGameModeCreative ( ) ) | | // In creative mode, digging is done immediately
2014-03-01 14:34:19 -05:00
cBlockInfo : : IsOneHitDig ( a_OldBlock ) // One-hit blocks get destroyed immediately, too
2013-02-14 16:47:57 -05:00
)
2012-06-14 09:06:06 -04:00
{
2013-01-11 23:46:01 -05:00
HandleBlockDigFinished ( a_BlockX , a_BlockY , a_BlockZ , a_BlockFace , a_OldBlock , a_OldMeta ) ;
2012-06-14 09:06:06 -04:00
return ;
}
2012-11-12 06:10:01 -05:00
2013-01-11 23:46:01 -05:00
// Start dig animation
// TODO: calculate real animation speed
// TODO: Send animation packets even without receiving any other packets
m_BlockDigAnimSpeed = 10 ;
m_BlockDigAnimX = a_BlockX ;
m_BlockDigAnimY = a_BlockY ;
m_BlockDigAnimZ = a_BlockZ ;
m_BlockDigAnimStage = 0 ;
m_Player - > GetWorld ( ) - > BroadcastBlockBreakAnimation ( m_UniqueID , m_BlockDigAnimX , m_BlockDigAnimY , m_BlockDigAnimZ , 0 , this ) ;
cWorld * World = m_Player - > GetWorld ( ) ;
2014-02-01 11:35:48 -05:00
cChunkInterface ChunkInterface ( World - > GetChunkMap ( ) ) ;
2014-03-02 14:25:05 -05:00
cBlockHandler * Handler = cBlockInfo : : GetHandler ( a_OldBlock ) ;
2014-02-01 11:35:48 -05:00
Handler - > OnDigging ( ChunkInterface , * World , m_Player , a_BlockX , a_BlockY , a_BlockZ ) ;
2013-01-11 23:46:01 -05:00
cItemHandler * ItemHandler = cItemHandler : : GetItemHandler ( m_Player - > GetEquippedItem ( ) ) ;
2013-05-19 14:22:37 -04:00
ItemHandler - > OnDiggingBlock ( World , m_Player , m_Player - > GetEquippedItem ( ) , a_BlockX , a_BlockY , a_BlockZ , a_BlockFace ) ;
2013-01-11 23:46:01 -05:00
// Check for clickthrough-blocks:
if ( a_BlockFace ! = BLOCK_FACE_NONE )
{
int pX = a_BlockX ;
int pY = a_BlockY ;
int pZ = a_BlockZ ;
2013-12-06 14:22:25 -05:00
AddFaceDirection ( pX , pY , pZ , a_BlockFace ) ; // Get the block in front of the clicked coordinates (m_bInverse defaulted to false)
2014-03-02 14:25:05 -05:00
Handler = cBlockInfo : : GetHandler ( World - > GetBlock ( pX , pY , pZ ) ) ;
2013-12-06 14:22:25 -05:00
2013-01-11 23:46:01 -05:00
if ( Handler - > IsClickedThrough ( ) )
{
2014-02-01 11:35:48 -05:00
Handler - > OnDigging ( ChunkInterface , * World , m_Player , pX , pY , pZ ) ;
2013-01-11 23:46:01 -05:00
}
}
}
2012-06-14 09:06:06 -04:00
2012-11-12 06:10:01 -05:00
2013-01-11 23:46:01 -05:00
2014-02-04 13:59:05 -05:00
void cClientHandle : : HandleBlockDigFinished ( int a_BlockX , int a_BlockY , int a_BlockZ , eBlockFace a_BlockFace , BLOCKTYPE a_OldBlock , NIBBLETYPE a_OldMeta )
2013-01-11 23:46:01 -05:00
{
if (
! m_HasStartedDigging | | // Hasn't received the DIG_STARTED packet
( m_LastDigBlockX ! = a_BlockX ) | | // DIG_STARTED has had different pos
( m_LastDigBlockY ! = a_BlockY ) | |
( m_LastDigBlockZ ! = a_BlockZ )
)
2012-09-25 05:54:36 -04:00
{
2013-01-11 23:46:01 -05:00
LOGD ( " Prevented a dig/aim bug in the client (finish {%d, %d, %d} vs start {%d, %d, %d}, HSD: %s) " ,
a_BlockX , a_BlockY , a_BlockZ ,
m_LastDigBlockX , m_LastDigBlockY , m_LastDigBlockZ ,
2014-03-11 17:16:08 -04:00
( m_HasStartedDigging ? " True " : " False " )
2013-01-11 23:46:01 -05:00
) ;
return ;
2012-09-25 05:54:36 -04:00
}
2013-01-11 23:46:01 -05:00
2014-05-11 05:56:15 -04:00
FinishDigAnimation ( ) ;
2012-09-25 05:54:36 -04:00
2014-05-15 13:58:48 -04:00
cWorld * World = m_Player - > GetWorld ( ) ;
2013-01-11 23:46:01 -05:00
cItemHandler * ItemHandler = cItemHandler : : GetItemHandler ( m_Player - > GetEquippedItem ( ) ) ;
2014-05-09 17:10:02 -04:00
if ( cRoot : : Get ( ) - > GetPluginManager ( ) - > CallHookPlayerBreakingBlock ( * m_Player , a_BlockX , a_BlockY , a_BlockZ , a_BlockFace , a_OldBlock , a_OldMeta ) )
2012-09-25 05:54:36 -04:00
{
2014-05-09 17:10:02 -04:00
// A plugin doesn't agree with the breaking. Bail out. Send the block back to the client, so that it knows:
m_Player - > GetWorld ( ) - > SendBlockTo ( a_BlockX , a_BlockY , a_BlockZ , m_Player ) ;
return ;
2012-09-25 05:54:36 -04:00
}
2013-01-11 23:46:01 -05:00
if ( a_OldBlock = = E_BLOCK_AIR )
2012-06-14 09:06:06 -04:00
{
2013-11-10 17:20:25 -05:00
LOGD ( " Dug air - what the function? " ) ;
2013-01-11 23:46:01 -05:00
return ;
2012-08-18 05:56:28 -04:00
}
2014-05-09 17:43:00 -04:00
2013-05-19 14:22:37 -04:00
ItemHandler - > OnBlockDestroyed ( World , m_Player , m_Player - > GetEquippedItem ( ) , a_BlockX , a_BlockY , a_BlockZ ) ;
2013-09-21 12:08:05 -04:00
// The ItemHandler is also responsible for spawning the pickups
2014-02-01 09:01:13 -05:00
cChunkInterface ChunkInterface ( World - > GetChunkMap ( ) ) ;
BlockHandler ( a_OldBlock ) - > OnDestroyedByPlayer ( ChunkInterface , * World , m_Player , a_BlockX , a_BlockY , a_BlockZ ) ;
2013-11-10 17:20:25 -05:00
World - > BroadcastSoundParticleEffect ( 2001 , a_BlockX , a_BlockY , a_BlockZ , a_OldBlock , this ) ;
2013-01-11 23:46:01 -05:00
World - > DigBlock ( a_BlockX , a_BlockY , a_BlockZ ) ;
2012-10-06 16:53:08 -04:00
2013-01-11 23:46:01 -05:00
cRoot : : Get ( ) - > GetPluginManager ( ) - > CallHookPlayerBrokenBlock ( * m_Player , a_BlockX , a_BlockY , a_BlockZ , a_BlockFace , a_OldBlock , a_OldMeta ) ;
2012-06-14 09:06:06 -04:00
}
2014-05-11 05:56:15 -04:00
void cClientHandle : : FinishDigAnimation ( )
2014-05-09 17:10:02 -04:00
{
if (
! m_HasStartedDigging | | // Hasn't received the DIG_STARTED packet
( m_LastDigBlockX = = - 1 ) | |
( m_LastDigBlockY = = - 1 ) | |
( m_LastDigBlockZ = = - 1 )
)
{
return ;
}
m_HasStartedDigging = false ;
if ( m_BlockDigAnimStage ! = - 1 )
{
// End dig animation
m_BlockDigAnimStage = - 1 ;
// It seems that 10 ends block animation
m_Player - > GetWorld ( ) - > BroadcastBlockBreakAnimation ( m_UniqueID , m_LastDigBlockX , m_LastDigBlockY , m_LastDigBlockZ , 10 , this ) ;
}
m_BlockDigAnimX = - 1 ;
m_BlockDigAnimY = - 1 ;
m_BlockDigAnimZ = - 1 ;
}
2014-02-04 13:59:05 -05:00
void cClientHandle : : HandleRightClick ( int a_BlockX , int a_BlockY , int a_BlockZ , eBlockFace a_BlockFace , int a_CursorX , int a_CursorY , int a_CursorZ , const cItem & a_HeldItem )
2012-06-14 09:06:06 -04:00
{
2013-01-11 23:46:01 -05:00
LOGD ( " HandleRightClick: {%d, %d, %d}, face %d, HeldItem: %s " ,
2012-11-18 16:50:05 -05:00
a_BlockX , a_BlockY , a_BlockZ , a_BlockFace , ItemToFullString ( a_HeldItem ) . c_str ( )
2012-09-07 16:53:37 -04:00
) ;
2014-03-05 09:10:20 -05:00
cWorld * World = m_Player - > GetWorld ( ) ;
2014-05-15 13:58:48 -04:00
if (
2014-06-08 05:32:52 -04:00
( a_BlockFace ! = BLOCK_FACE_NONE ) & & // The client is interacting with a specific block
(
( Diff ( m_Player - > GetPosX ( ) , ( double ) a_BlockX ) > 6 ) | | // The block is too far away
( Diff ( m_Player - > GetPosY ( ) , ( double ) a_BlockY ) > 6 ) | |
( Diff ( m_Player - > GetPosZ ( ) , ( double ) a_BlockZ ) > 6 )
)
2014-05-15 13:58:48 -04:00
)
{
2014-06-08 05:32:52 -04:00
AddFaceDirection ( a_BlockX , a_BlockY , a_BlockZ , a_BlockFace ) ;
World - > SendBlockTo ( a_BlockX , a_BlockY , a_BlockZ , m_Player ) ;
if ( a_BlockY < cChunkDef : : Height - 1 )
2014-05-15 13:58:48 -04:00
{
World - > SendBlockTo ( a_BlockX , a_BlockY + 1 , a_BlockZ , m_Player ) ; // 2 block high things
}
2014-06-08 05:32:52 -04:00
if ( a_BlockY > 0 )
{
World - > SendBlockTo ( a_BlockX , a_BlockY - 1 , a_BlockZ , m_Player ) ; // 2 block high things
}
m_Player - > GetInventory ( ) . SendEquippedSlot ( ) ;
2014-05-15 13:58:48 -04:00
return ;
}
2013-01-11 23:46:01 -05:00
cPluginManager * PlgMgr = cRoot : : Get ( ) - > GetPluginManager ( ) ;
if ( PlgMgr - > CallHookPlayerRightClick ( * m_Player , a_BlockX , a_BlockY , a_BlockZ , a_BlockFace , a_CursorX , a_CursorY , a_CursorZ ) )
{
// A plugin doesn't agree with the action, replace the block on the client and quit:
2014-03-05 09:10:20 -05:00
cChunkInterface ChunkInterface ( World - > GetChunkMap ( ) ) ;
BLOCKTYPE BlockType = World - > GetBlock ( a_BlockX , a_BlockY , a_BlockZ ) ;
cBlockHandler * BlockHandler = cBlockInfo : : GetHandler ( BlockType ) ;
BlockHandler - > OnCancelRightClick ( ChunkInterface , * World , m_Player , a_BlockX , a_BlockY , a_BlockZ , a_BlockFace ) ;
2014-03-09 10:55:47 -04:00
if ( a_BlockFace ! = BLOCK_FACE_NONE )
2013-01-11 23:46:01 -05:00
{
AddFaceDirection ( a_BlockX , a_BlockY , a_BlockZ , a_BlockFace ) ;
2014-03-05 09:10:20 -05:00
World - > SendBlockTo ( a_BlockX , a_BlockY , a_BlockZ , m_Player ) ;
2014-05-15 13:58:48 -04:00
World - > SendBlockTo ( a_BlockX , a_BlockY + 1 , a_BlockZ , m_Player ) ; // 2 block high things
2014-05-09 17:10:02 -04:00
m_Player - > GetInventory ( ) . SendEquippedSlot ( ) ;
2013-01-11 23:46:01 -05:00
}
return ;
}
2014-03-28 18:52:04 -04:00
m_NumBlockChangeInteractionsThisTick + + ;
2013-01-11 23:46:01 -05:00
2012-06-14 09:06:06 -04:00
if ( ! CheckBlockInteractionsRate ( ) )
{
2014-03-02 11:28:51 -05:00
Kick ( " Too many blocks were placed/interacted with per unit time - hacked client? " ) ;
2012-06-14 09:06:06 -04:00
return ;
}
2013-05-19 14:22:37 -04:00
const cItem & Equipped = m_Player - > GetInventory ( ) . GetEquippedItem ( ) ;
2012-06-14 09:06:06 -04:00
2012-09-07 16:53:37 -04:00
if ( ( Equipped . m_ItemType ! = a_HeldItem . m_ItemType ) & & ( a_HeldItem . m_ItemType ! = - 1 ) )
2012-06-14 09:06:06 -04:00
{
2012-09-07 16:53:37 -04:00
// Only compare ItemType, not meta (torches have different metas)
// The -1 check is there because sometimes the client sends -1 instead of the held item
// ( http://forum.mc-server.org/showthread.php?tid=549&pid=4502#pid4502 )
2012-06-14 09:06:06 -04:00
LOGWARN ( " Player %s tried to place a block that was not equipped (exp %d, got %d) " ,
2012-08-18 05:56:28 -04:00
m_Username . c_str ( ) , Equipped . m_ItemType , a_HeldItem . m_ItemType
2012-06-14 09:06:06 -04:00
) ;
2012-08-18 05:56:28 -04:00
// Let's send the current world block to the client, so that it can immediately "let the user know" that they haven't placed the block
2014-03-29 09:18:26 -04:00
if ( a_BlockFace ! = BLOCK_FACE_NONE )
2012-08-18 05:56:28 -04:00
{
2013-01-11 23:46:01 -05:00
AddFaceDirection ( a_BlockX , a_BlockY , a_BlockZ , a_BlockFace ) ;
2014-03-05 09:10:20 -05:00
World - > SendBlockTo ( a_BlockX , a_BlockY , a_BlockZ , m_Player ) ;
2012-08-18 05:56:28 -04:00
}
2012-06-14 09:06:06 -04:00
return ;
}
2013-01-11 23:46:01 -05:00
BLOCKTYPE BlockType ;
NIBBLETYPE BlockMeta ;
World - > GetBlockTypeMeta ( a_BlockX , a_BlockY , a_BlockZ , BlockType , BlockMeta ) ;
2014-03-02 14:25:05 -05:00
cBlockHandler * BlockHandler = cBlockInfo : : GetHandler ( BlockType ) ;
2012-12-19 16:19:36 -05:00
2013-08-30 08:24:03 -04:00
if ( BlockHandler - > IsUseable ( ) & & ! m_Player - > IsCrouched ( ) )
2012-06-14 09:06:06 -04:00
{
2013-01-11 23:46:01 -05:00
if ( PlgMgr - > CallHookPlayerUsingBlock ( * m_Player , a_BlockX , a_BlockY , a_BlockZ , a_BlockFace , a_CursorX , a_CursorY , a_CursorZ , BlockType , BlockMeta ) )
{
// A plugin doesn't agree with using the block, abort
return ;
}
2014-02-01 08:06:32 -05:00
cChunkInterface ChunkInterface ( World - > GetChunkMap ( ) ) ;
BlockHandler - > OnUse ( ChunkInterface , * World , m_Player , a_BlockX , a_BlockY , a_BlockZ , a_BlockFace , a_CursorX , a_CursorY , a_CursorZ ) ;
2013-01-11 23:46:01 -05:00
PlgMgr - > CallHookPlayerUsedBlock ( * m_Player , a_BlockX , a_BlockY , a_BlockZ , a_BlockFace , a_CursorX , a_CursorY , a_CursorZ , BlockType , BlockMeta ) ;
return ;
2012-08-06 16:10:16 -04:00
}
2013-01-11 23:46:01 -05:00
cItemHandler * ItemHandler = cItemHandler : : GetItemHandler ( Equipped . m_ItemType ) ;
2014-03-29 09:18:26 -04:00
if ( ItemHandler - > IsPlaceable ( ) & & ( a_BlockFace ! = BLOCK_FACE_NONE ) )
2012-08-06 16:10:16 -04:00
{
2013-01-11 23:46:01 -05:00
HandlePlaceBlock ( a_BlockX , a_BlockY , a_BlockZ , a_BlockFace , a_CursorX , a_CursorY , a_CursorZ , * ItemHandler ) ;
}
2014-06-07 19:32:37 -04:00
else if ( ( ItemHandler - > IsFood ( ) | | ItemHandler - > IsDrinkable ( & Equipped ) ) & & ! m_Player - > IsGameModeCreative ( ) )
2013-01-11 23:46:01 -05:00
{
2014-06-07 19:32:37 -04:00
if ( m_Player - > IsSatiated ( ) & & ! ItemHandler - > IsDrinkable ( & Equipped ) )
2013-07-28 16:55:09 -04:00
{
// The player is satiated, they cannot eat
return ;
}
2013-07-28 13:15:03 -04:00
m_Player - > StartEating ( ) ;
if ( PlgMgr - > CallHookPlayerEating ( * m_Player ) )
2012-06-14 09:06:06 -04:00
{
2013-07-28 13:15:03 -04:00
// A plugin won't let us eat, abort (send the proper packets to the client, too):
m_Player - > AbortEating ( ) ;
2013-01-11 23:46:01 -05:00
return ;
2012-08-18 05:56:28 -04:00
}
2013-07-28 13:15:03 -04:00
return ;
2013-01-11 23:46:01 -05:00
}
else
{
if ( PlgMgr - > CallHookPlayerUsingItem ( * m_Player , a_BlockX , a_BlockY , a_BlockZ , a_BlockFace , a_CursorX , a_CursorY , a_CursorZ ) )
2012-06-14 09:06:06 -04:00
{
2013-01-11 23:46:01 -05:00
// A plugin doesn't agree with using the item, abort
return ;
}
2013-05-19 14:22:37 -04:00
ItemHandler - > OnItemUse ( World , m_Player , Equipped , a_BlockX , a_BlockY , a_BlockZ , a_BlockFace ) ;
2013-01-11 23:46:01 -05:00
PlgMgr - > CallHookPlayerUsedItem ( * m_Player , a_BlockX , a_BlockY , a_BlockZ , a_BlockFace , a_CursorX , a_CursorY , a_CursorZ ) ;
}
}
2012-06-14 09:06:06 -04:00
2012-07-15 16:36:34 -04:00
2012-06-14 09:06:06 -04:00
2014-02-04 13:59:05 -05:00
void cClientHandle : : HandlePlaceBlock ( int a_BlockX , int a_BlockY , int a_BlockZ , eBlockFace a_BlockFace , int a_CursorX , int a_CursorY , int a_CursorZ , cItemHandler & a_ItemHandler )
2013-01-11 23:46:01 -05:00
{
2014-03-23 18:32:45 -04:00
BLOCKTYPE EquippedBlock = ( BLOCKTYPE ) ( m_Player - > GetEquippedItem ( ) . m_ItemType ) ;
2013-01-11 23:46:01 -05:00
if ( a_BlockFace < 0 )
{
// Clicked in air
return ;
}
cWorld * World = m_Player - > GetWorld ( ) ;
2013-08-23 14:38:39 -04:00
BLOCKTYPE ClickedBlock ;
NIBBLETYPE ClickedBlockMeta ;
NIBBLETYPE EquippedBlockDamage = ( NIBBLETYPE ) ( m_Player - > GetEquippedItem ( ) . m_ItemDamage ) ;
2013-08-25 10:11:19 -04:00
if ( ( a_BlockY < 0 ) | | ( a_BlockY > = cChunkDef : : Height ) )
{
// The block is being placed outside the world, ignore this packet altogether (#128)
return ;
}
2013-01-11 23:46:01 -05:00
2013-08-23 14:38:39 -04:00
World - > GetBlockTypeMeta ( a_BlockX , a_BlockY , a_BlockZ , ClickedBlock , ClickedBlockMeta ) ;
2013-08-25 10:11:19 -04:00
// Special slab handling - placing a slab onto another slab produces a dblslab instead:
2013-08-23 14:38:39 -04:00
if (
2013-08-25 10:11:19 -04:00
cBlockSlabHandler : : IsAnySlabType ( ClickedBlock ) & & // Is there a slab already?
cBlockSlabHandler : : IsAnySlabType ( EquippedBlock ) & & // Is the player placing another slab?
2014-02-28 16:32:10 -05:00
( ( ClickedBlockMeta & 0x07 ) = = EquippedBlockDamage ) & & // Is it the same slab type?
2013-08-25 10:11:19 -04:00
(
2014-03-28 18:52:04 -04:00
( a_BlockFace = = BLOCK_FACE_TOP ) | | // Clicking the top of a bottom slab
( a_BlockFace = = BLOCK_FACE_BOTTOM ) // Clicking the bottom of a top slab
2013-08-23 14:38:39 -04:00
)
2013-08-25 10:11:19 -04:00
)
2013-01-11 23:46:01 -05:00
{
2013-12-06 14:22:25 -05:00
// Coordinates at clicked block, which was an eligible slab, and either top or bottom faces were clicked
// If clicked top face and slab occupies the top voxel, we want a slab to be placed above it (therefore increment Y)
// Else if clicked bottom face and slab occupies the bottom voxel, decrement Y for the same reason
// Don't touch coordinates if anything else because a dblslab opportunity is present
2014-03-31 15:34:11 -04:00
if ( ( ClickedBlockMeta & 0x08 ) & & ( a_BlockFace = = BLOCK_FACE_TOP ) )
2013-12-02 11:32:28 -05:00
{
+ + a_BlockY ;
}
else if ( ! ( ClickedBlockMeta & 0x08 ) & & ( a_BlockFace = = BLOCK_FACE_BOTTOM ) )
{
- - a_BlockY ;
}
World - > GetBlockTypeMeta ( a_BlockX , a_BlockY , a_BlockZ , ClickedBlock , ClickedBlockMeta ) ;
2013-01-11 23:46:01 -05:00
}
else
{
2013-08-24 13:46:19 -04:00
// Check if the block ignores build collision (water, grass etc.):
2013-11-29 19:31:21 -05:00
if (
BlockHandler ( ClickedBlock ) - > DoesIgnoreBuildCollision ( ) | |
BlockHandler ( ClickedBlock ) - > DoesIgnoreBuildCollision ( m_Player , ClickedBlockMeta )
)
2013-01-11 23:46:01 -05:00
{
2014-02-01 09:01:13 -05:00
cChunkInterface ChunkInterface ( World - > GetChunkMap ( ) ) ;
BlockHandler ( ClickedBlock ) - > OnDestroyedByPlayer ( ChunkInterface , * World , m_Player , a_BlockX , a_BlockY , a_BlockZ ) ;
2013-01-11 23:46:01 -05:00
}
2013-11-29 19:31:21 -05:00
else
2012-08-18 05:56:28 -04:00
{
2013-08-23 14:38:39 -04:00
AddFaceDirection ( a_BlockX , a_BlockY , a_BlockZ , a_BlockFace ) ;
2013-08-24 13:46:19 -04:00
2013-08-25 10:11:19 -04:00
if ( ( a_BlockY < 0 ) | | ( a_BlockY > = cChunkDef : : Height ) )
{
// The block is being placed outside the world, ignore this packet altogether (#128)
return ;
}
2013-11-29 19:31:21 -05:00
NIBBLETYPE PlaceMeta ;
BLOCKTYPE PlaceBlock ;
World - > GetBlockTypeMeta ( a_BlockX , a_BlockY , a_BlockZ , PlaceBlock , PlaceMeta ) ;
2013-08-25 10:11:19 -04:00
2013-08-24 13:46:19 -04:00
// Clicked on side of block, make sure that placement won't be cancelled if there is a slab able to be double slabbed.
// No need to do combinability (dblslab) checks, client will do that here.
2013-08-25 10:11:19 -04:00
if ( cBlockSlabHandler : : IsAnySlabType ( PlaceBlock ) )
2013-08-23 14:38:39 -04:00
{
2013-08-25 10:11:19 -04:00
// It's a slab, don't do checks and proceed to double-slabbing
2013-08-23 14:38:39 -04:00
}
else
{
2013-11-29 19:31:21 -05:00
if (
! BlockHandler ( PlaceBlock ) - > DoesIgnoreBuildCollision ( ) & &
! BlockHandler ( PlaceBlock ) - > DoesIgnoreBuildCollision ( m_Player , PlaceMeta )
)
2013-08-23 14:38:39 -04:00
{
// Tried to place a block *into* another?
2013-11-29 19:31:21 -05:00
// Happens when you place a block aiming at side of block with a torch on it or stem beside it
2013-08-23 14:38:39 -04:00
return ;
}
}
2012-06-14 09:06:06 -04:00
}
}
2013-08-24 13:46:19 -04:00
2013-01-11 23:46:01 -05:00
BLOCKTYPE BlockType ;
NIBBLETYPE BlockMeta ;
if ( ! a_ItemHandler . GetPlacementBlockTypeMeta ( World , m_Player , a_BlockX , a_BlockY , a_BlockZ , a_BlockFace , a_CursorX , a_CursorY , a_CursorZ , BlockType , BlockMeta ) )
{
// Handler refused the placement, send that information back to the client:
2014-06-04 06:29:08 -04:00
World - > SendBlockTo ( a_BlockX , a_BlockY , a_BlockZ , m_Player ) ;
2014-05-09 17:10:02 -04:00
m_Player - > GetInventory ( ) . SendEquippedSlot ( ) ;
2013-01-11 23:46:01 -05:00
return ;
}
cBlockHandler * NewBlock = BlockHandler ( BlockType ) ;
if ( cRoot : : Get ( ) - > GetPluginManager ( ) - > CallHookPlayerPlacingBlock ( * m_Player , a_BlockX , a_BlockY , a_BlockZ , a_BlockFace , a_CursorX , a_CursorY , a_CursorZ , BlockType , BlockMeta ) )
{
// A plugin doesn't agree with placing the block, revert the block on the client:
World - > SendBlockTo ( a_BlockX , a_BlockY , a_BlockZ , m_Player ) ;
2014-05-09 17:10:02 -04:00
m_Player - > GetInventory ( ) . SendEquippedSlot ( ) ;
2013-01-11 23:46:01 -05:00
return ;
}
// The actual block placement:
World - > SetBlock ( a_BlockX , a_BlockY , a_BlockZ , BlockType , BlockMeta ) ;
2014-03-29 06:25:40 -04:00
if ( ! m_Player - > IsGameModeCreative ( ) )
2013-01-11 23:46:01 -05:00
{
2013-05-24 03:30:39 -04:00
m_Player - > GetInventory ( ) . RemoveOneEquippedItem ( ) ;
2013-01-11 23:46:01 -05:00
}
2014-01-31 18:17:41 -05:00
cChunkInterface ChunkInterface ( World - > GetChunkMap ( ) ) ;
2014-02-01 08:06:32 -05:00
NewBlock - > OnPlacedByPlayer ( ChunkInterface , * World , m_Player , a_BlockX , a_BlockY , a_BlockZ , a_BlockFace , a_CursorX , a_CursorY , a_CursorZ , BlockType , BlockMeta ) ;
2013-01-11 23:46:01 -05:00
// Step sound with 0.8f pitch is used as block placement sound
2013-08-25 10:11:19 -04:00
World - > BroadcastSoundEffect ( NewBlock - > GetStepSound ( ) , a_BlockX * 8 , a_BlockY * 8 , a_BlockZ * 8 , 1.0f , 0.8f ) ;
2013-01-11 23:46:01 -05:00
cRoot : : Get ( ) - > GetPluginManager ( ) - > CallHookPlayerPlacedBlock ( * m_Player , a_BlockX , a_BlockY , a_BlockZ , a_BlockFace , a_CursorX , a_CursorY , a_CursorZ , BlockType , BlockMeta ) ;
2012-06-14 09:06:06 -04:00
}
2012-08-18 06:38:15 -04:00
void cClientHandle : : HandleChat ( const AString & a_Message )
2012-06-14 09:06:06 -04:00
{
2013-08-14 14:08:05 -04:00
// We no longer need to postpone message processing, because the messages already arrive in the Tick thread
2013-07-03 03:47:35 -04:00
2013-08-14 14:08:05 -04:00
// If a command, perform it:
AString Message ( a_Message ) ;
if ( cRoot : : Get ( ) - > GetServer ( ) - > Command ( * this , Message ) )
{
return ;
}
2014-02-16 17:51:32 -05:00
// Not a command, broadcast as a message:
cCompositeChat Msg ;
AString Color = m_Player - > GetColor ( ) ;
if ( Color . length ( ) = = 3 )
{
Color = AString ( " @ " ) + Color [ 2 ] ;
}
else
2014-04-18 15:09:44 -04:00
{
Color . clear ( ) ;
2014-02-16 17:51:32 -05:00
}
Msg . AddTextPart ( AString ( " < " ) + m_Player - > GetName ( ) + " > " , Color ) ;
Msg . ParseText ( a_Message ) ;
Msg . UnderlineUrls ( ) ;
2013-08-14 14:08:05 -04:00
m_Player - > GetWorld ( ) - > BroadcastChat ( Msg ) ;
2012-06-14 09:06:06 -04:00
}
2012-08-19 07:51:17 -04:00
void cClientHandle : : HandlePlayerLook ( float a_Rotation , float a_Pitch , bool a_IsOnGround )
2012-06-14 09:06:06 -04:00
{
2013-09-08 12:36:06 -04:00
if ( ( m_Player = = NULL ) | | ( m_State ! = csPlaying ) )
{
return ;
}
2014-01-16 14:00:49 -05:00
m_Player - > SetYaw ( a_Rotation ) ;
2013-04-02 02:48:31 -04:00
m_Player - > SetHeadYaw ( a_Rotation ) ;
2012-08-19 07:51:17 -04:00
m_Player - > SetPitch ( a_Pitch ) ;
m_Player - > SetTouchGround ( a_IsOnGround ) ;
2012-06-14 09:06:06 -04:00
}
2014-05-26 10:38:14 -04:00
void cClientHandle : : HandlePlayerMoveLook ( double a_PosX , double a_PosY , double a_PosZ , double a_Stance , float a_Rotation , float a_Pitch , bool a_IsOnGround )
2012-06-14 09:06:06 -04:00
{
2014-05-26 17:13:40 -04:00
HandlePlayerLook ( a_Rotation , a_Pitch , a_IsOnGround ) ;
2014-05-27 03:28:46 -04:00
HandlePlayerPos ( a_PosX , a_PosY , a_PosZ , a_Stance , a_IsOnGround ) ;
2012-06-14 09:06:06 -04:00
}
2012-08-19 07:51:17 -04:00
void cClientHandle : : HandleAnimation ( char a_Animation )
2012-06-14 09:06:06 -04:00
{
2013-12-18 11:11:15 -05:00
if ( cPluginManager : : Get ( ) - > CallHookPlayerAnimation ( * m_Player , a_Animation ) )
2013-08-11 06:12:20 -04:00
{
// Plugin disagrees, bail out
return ;
}
2013-12-19 11:03:43 -05:00
// Because the animation ID sent to servers by clients are different to those sent back, we need this
switch ( a_Animation )
{
case 0 : // No animation - wiki.vg doesn't say that client has something specific for it, so I suppose it will just become -1
case 1 :
case 2 :
case 3 :
{
a_Animation - - ; // Offset by -1
break ;
}
case 5 :
case 6 :
case 7 :
{
a_Animation - = 2 ; // Offset by -2
break ;
}
default : // Anything else is the same
break ;
}
2013-12-06 18:47:07 -05:00
m_Player - > GetWorld ( ) - > BroadcastEntityAnimation ( * m_Player , a_Animation , this ) ;
2012-06-14 09:06:06 -04:00
}
2012-08-19 07:51:17 -04:00
void cClientHandle : : HandleSlotSelected ( short a_SlotNum )
2012-06-14 09:06:06 -04:00
{
2013-05-19 14:22:37 -04:00
m_Player - > GetInventory ( ) . SetEquippedSlotNum ( a_SlotNum ) ;
2012-08-19 07:51:17 -04:00
m_Player - > GetWorld ( ) - > BroadcastEntityEquipment ( * m_Player , 0 , m_Player - > GetInventory ( ) . GetEquippedItem ( ) , this ) ;
2012-06-14 09:06:06 -04:00
}
2013-09-05 18:04:49 -04:00
void cClientHandle : : HandleSteerVehicle ( float a_Forward , float a_Sideways )
{
m_Player - > SteerVehicle ( a_Forward , a_Sideways ) ;
}
2012-08-19 07:51:17 -04:00
void cClientHandle : : HandleWindowClose ( char a_WindowID )
2012-06-14 09:06:06 -04:00
{
2013-05-30 15:34:09 -04:00
m_Player - > CloseWindowIfID ( a_WindowID ) ;
2012-06-14 09:06:06 -04:00
}
2013-05-08 05:45:07 -04:00
void cClientHandle : : HandleWindowClick ( char a_WindowID , short a_SlotNum , eClickAction a_ClickAction , const cItem & a_HeldItem )
2012-06-14 09:06:06 -04:00
{
2013-05-08 05:45:07 -04:00
LOGD ( " WindowClick: WinID %d, SlotNum %d, action: %s, Item %s x %d " ,
a_WindowID , a_SlotNum , ClickActionToString ( a_ClickAction ) ,
2012-09-20 09:25:54 -04:00
ItemToString ( a_HeldItem ) . c_str ( ) , a_HeldItem . m_ItemCount
) ;
2012-06-14 09:06:06 -04:00
cWindow * Window = m_Player - > GetWindow ( ) ;
if ( Window = = NULL )
{
LOGWARNING ( " Player \" %s \" clicked in a non-existent window. Ignoring " , m_Username . c_str ( ) ) ;
return ;
}
2013-05-08 05:45:07 -04:00
Window - > Clicked ( * m_Player , a_WindowID , a_SlotNum , a_ClickAction , a_HeldItem ) ;
2012-06-14 09:06:06 -04:00
}
2012-08-19 15:42:32 -04:00
void cClientHandle : : 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
)
2012-06-14 09:06:06 -04:00
{
cWorld * World = m_Player - > GetWorld ( ) ;
2012-09-01 17:44:18 -04:00
World - > UpdateSign ( a_BlockX , a_BlockY , a_BlockZ , a_Line1 , a_Line2 , a_Line3 , a_Line4 , m_Player ) ;
2012-06-14 09:06:06 -04:00
}
2012-08-19 15:42:32 -04:00
void cClientHandle : : HandleUseEntity ( int a_TargetEntityID , bool a_IsLeftClick )
2012-06-14 09:06:06 -04:00
{
2013-03-03 14:05:11 -05:00
// TODO: Let plugins interfere via a hook
2013-07-12 16:01:25 -04:00
// If it is a right click, call the entity's OnRightClicked() handler:
2012-08-19 15:42:32 -04:00
if ( ! a_IsLeftClick )
2012-06-14 09:06:06 -04:00
{
2013-03-03 14:05:11 -05:00
class cRclkEntity : public cEntityCallback
{
cPlayer & m_Player ;
virtual bool Item ( cEntity * a_Entity ) override
{
2013-08-02 02:44:06 -04:00
if ( cPluginManager : : Get ( ) - > CallHookPlayerRightClickingEntity ( m_Player , * a_Entity ) )
{
return false ;
}
2013-03-03 14:05:11 -05:00
a_Entity - > OnRightClicked ( m_Player ) ;
2013-07-12 16:01:25 -04:00
return false ;
2013-03-03 14:05:11 -05:00
}
public :
cRclkEntity ( cPlayer & a_Player ) : m_Player ( a_Player ) { }
} Callback ( * m_Player ) ;
cWorld * World = m_Player - > GetWorld ( ) ;
World - > DoWithEntityByID ( a_TargetEntityID , Callback ) ;
2012-06-14 09:06:06 -04:00
return ;
}
2013-07-12 16:01:25 -04:00
// If it is a left click, attack the entity:
2012-06-14 09:06:06 -04:00
class cDamageEntity : public cEntityCallback
{
virtual bool Item ( cEntity * a_Entity ) override
{
2012-11-16 11:03:56 -05:00
if ( ! a_Entity - > GetWorld ( ) - > IsPVPEnabled ( ) )
2012-10-10 15:46:12 -04:00
{
2013-07-12 16:01:25 -04:00
// PVP is disabled, disallow players hurting other players:
if ( a_Entity - > IsPlayer ( ) )
2012-10-10 15:46:12 -04:00
{
// Player is hurting another player which is not allowed when PVP is disabled so ignore it
return true ;
}
}
2013-07-12 16:01:25 -04:00
a_Entity - > TakeDamage ( * m_Attacker ) ;
return false ;
2012-06-14 09:06:06 -04:00
}
public :
2012-12-21 06:04:08 -05:00
cPawn * m_Attacker ;
2012-06-14 09:06:06 -04:00
} Callback ;
2012-12-21 06:04:08 -05:00
Callback . m_Attacker = m_Player ;
2012-06-14 09:06:06 -04:00
cWorld * World = m_Player - > GetWorld ( ) ;
2013-07-12 16:01:25 -04:00
if ( World - > DoWithEntityByID ( a_TargetEntityID , Callback ) )
{
// Any kind of an attack implies food exhaustion
m_Player - > AddFoodExhaustion ( 0.3 ) ;
}
2012-06-14 09:06:06 -04:00
}
void cClientHandle : : HandleRespawn ( void )
{
2013-09-08 12:36:06 -04:00
if ( m_Player = = NULL )
2012-10-17 08:19:20 -04:00
{
Destroy ( ) ;
return ;
}
2012-06-14 09:06:06 -04:00
m_Player - > Respawn ( ) ;
2013-01-11 23:46:01 -05:00
cRoot : : Get ( ) - > GetPluginManager ( ) - > CallHookPlayerSpawned ( * m_Player ) ;
2012-06-14 09:06:06 -04:00
}
2012-08-19 15:42:32 -04:00
void cClientHandle : : HandleDisconnect ( const AString & a_Reason )
2012-06-14 09:06:06 -04:00
{
2013-12-18 13:17:17 -05:00
LOGD ( " Received d/c packet from %s with reason \" %s \" " , m_Username . c_str ( ) , a_Reason . c_str ( ) ) ;
2014-02-03 16:12:44 -05:00
2014-05-02 05:18:02 -04:00
cRoot : : Get ( ) - > GetPluginManager ( ) - > CallHookDisconnect ( * this , a_Reason ) ;
2014-02-03 16:12:44 -05:00
2013-04-13 17:02:10 -04:00
m_HasSentDC = true ;
2012-06-14 09:06:06 -04:00
Destroy ( ) ;
}
2012-08-19 15:42:32 -04:00
void cClientHandle : : HandleKeepAlive ( int a_KeepAliveID )
2012-06-14 09:06:06 -04:00
{
2012-08-19 15:42:32 -04:00
if ( a_KeepAliveID = = m_PingID )
2012-06-14 09:06:06 -04:00
{
cTimer t1 ;
m_Ping = ( short ) ( ( t1 . GetNowTime ( ) - m_PingStartTime ) / 2 ) ;
}
}
2012-08-19 15:42:32 -04:00
2012-09-05 16:30:27 -04:00
bool cClientHandle : : HandleHandshake ( const AString & a_Username )
{
if ( ! cRoot : : Get ( ) - > GetPluginManager ( ) - > CallHookHandshake ( this , a_Username ) )
{
2013-08-11 13:18:06 -04:00
if ( cRoot : : Get ( ) - > GetServer ( ) - > GetNumPlayers ( ) > = cRoot : : Get ( ) - > GetServer ( ) - > GetMaxPlayers ( ) )
2012-09-05 16:30:27 -04:00
{
Kick ( " The server is currently full :(-- Try again later " ) ;
return false ;
}
}
return true ;
}
2014-03-08 11:55:53 -05:00
void cClientHandle : : HandleEntityCrouch ( int a_EntityID , bool a_IsCrouching )
2012-09-29 16:43:42 -04:00
{
2013-08-11 13:18:06 -04:00
if ( a_EntityID ! = m_Player - > GetUniqueID ( ) )
2012-09-29 16:43:42 -04:00
{
// We should only receive entity actions from the entity that is performing the action
return ;
}
2014-03-08 11:55:53 -05:00
m_Player - > SetCrouch ( a_IsCrouching ) ;
}
void cClientHandle : : HandleEntityLeaveBed ( int a_EntityID )
{
if ( a_EntityID ! = m_Player - > GetUniqueID ( ) )
2012-09-29 16:43:42 -04:00
{
2014-03-08 11:55:53 -05:00
// We should only receive entity actions from the entity that is performing the action
return ;
2012-09-29 16:43:42 -04:00
}
2014-03-08 11:55:53 -05:00
m_Player - > GetWorld ( ) - > BroadcastEntityAnimation ( * m_Player , 2 ) ;
}
void cClientHandle : : HandleEntitySprinting ( int a_EntityID , bool a_IsSprinting )
{
if ( a_EntityID ! = m_Player - > GetUniqueID ( ) )
{
// We should only receive entity actions from the entity that is performing the action
return ;
}
m_Player - > SetSprint ( a_IsSprinting ) ;
2012-09-29 16:43:42 -04:00
}
2013-07-05 17:11:06 -04:00
void cClientHandle : : HandleUnmount ( void )
{
if ( m_Player = = NULL )
{
return ;
}
m_Player - > Detach ( ) ;
}
2013-07-30 16:48:59 -04:00
void cClientHandle : : HandleTabCompletion ( const AString & a_Text )
{
AStringVector Results ;
m_Player - > GetWorld ( ) - > TabCompleteUserName ( a_Text , Results ) ;
2013-07-31 05:16:11 -04:00
cRoot : : Get ( ) - > GetPluginManager ( ) - > TabCompleteCommand ( a_Text , Results , m_Player ) ;
2013-07-30 16:48:59 -04:00
if ( Results . empty ( ) )
{
return ;
}
std : : sort ( Results . begin ( ) , Results . end ( ) ) ;
SendTabCompletionResults ( Results ) ;
}
2014-04-04 04:13:25 -04:00
void cClientHandle : : SendData ( const char * a_Data , size_t a_Size )
2012-08-27 13:31:16 -04:00
{
2013-08-22 02:17:26 -04:00
if ( m_HasSentDC )
{
// This could crash the client, because they've already unloaded the world etc., and suddenly a wild packet appears (#31)
return ;
}
2012-08-27 13:31:16 -04:00
{
cCSLock Lock ( m_CSOutgoingData ) ;
2012-09-06 04:33:43 -04:00
// _X 2012_09_06: We need an overflow buffer, usually when streaming the initial chunks
if ( m_OutgoingDataOverflow . empty ( ) )
{
// No queued overflow data; if this packet fits into the ringbuffer, put it in, otherwise put it in the overflow buffer:
2014-04-04 04:13:25 -04:00
size_t CanFit = m_OutgoingData . GetFreeSpace ( ) ;
2012-09-06 04:33:43 -04:00
if ( CanFit > a_Size )
{
CanFit = a_Size ;
}
if ( CanFit > 0 )
{
m_OutgoingData . Write ( a_Data , CanFit ) ;
}
if ( a_Size > CanFit )
{
m_OutgoingDataOverflow . append ( a_Data + CanFit , a_Size - CanFit ) ;
}
}
else
2012-08-27 13:31:16 -04:00
{
2012-09-06 04:33:43 -04:00
// There is a queued overflow. Append to it, then send as much from its front as possible
m_OutgoingDataOverflow . append ( a_Data , a_Size ) ;
2014-05-01 16:58:58 -04:00
size_t CanFit = m_OutgoingData . GetFreeSpace ( ) ;
2012-09-06 04:33:43 -04:00
if ( CanFit > 128 )
{
// No point in moving the data over if it's not large enough - too much effort for too little an effect
m_OutgoingData . Write ( m_OutgoingDataOverflow . data ( ) , CanFit ) ;
m_OutgoingDataOverflow . erase ( 0 , CanFit ) ;
}
2012-08-27 13:31:16 -04:00
}
2012-09-06 04:33:43 -04:00
} // Lock(m_CSOutgoingData)
2012-08-27 13:31:16 -04:00
// Notify SocketThreads that we have something to write:
cRoot : : Get ( ) - > GetServer ( ) - > NotifyClientWrite ( this ) ;
}
2014-06-08 15:58:08 -04:00
void cClientHandle : : RemoveFromWorld ( void )
2013-08-14 04:24:34 -04:00
{
// Remove all associated chunks:
cChunkCoordsList Chunks ;
{
cCSLock Lock ( m_CSChunkLists ) ;
std : : swap ( Chunks , m_LoadedChunks ) ;
m_ChunksToSend . clear ( ) ;
}
for ( cChunkCoordsList : : iterator itr = Chunks . begin ( ) , end = Chunks . end ( ) ; itr ! = end ; + + itr )
{
m_Protocol - > SendUnloadChunk ( itr - > m_ChunkX , itr - > m_ChunkZ ) ;
} // for itr - Chunks[]
2014-03-28 21:21:56 -04:00
// StreamChunks() called in cPlayer::MoveToWorld() after new world has been set
// Meanwhile here, we set last streamed values to bogus ones so everything is resent
2013-08-14 04:24:34 -04:00
m_LastStreamedChunkX = 0x7fffffff ;
m_LastStreamedChunkZ = 0x7fffffff ;
2013-09-08 12:36:06 -04:00
m_HasSentPlayerChunk = false ;
2013-08-14 04:24:34 -04:00
}
2012-06-14 09:06:06 -04:00
bool cClientHandle : : CheckBlockInteractionsRate ( void )
{
ASSERT ( m_Player ! = NULL ) ;
ASSERT ( m_Player - > GetWorld ( ) ! = NULL ) ;
2014-03-01 16:24:36 -05:00
if ( m_NumBlockChangeInteractionsThisTick > MAX_BLOCK_CHANGE_INTERACTIONS )
2012-06-14 09:06:06 -04:00
{
2014-03-01 16:24:36 -05:00
return false ;
2012-06-14 09:06:06 -04:00
}
2014-03-01 16:24:36 -05:00
2012-06-14 09:06:06 -04:00
return true ;
}
void cClientHandle : : Tick ( float a_Dt )
{
2013-08-20 16:27:29 -04:00
// Handle clients that are waiting for final close while destroyed:
if ( m_State = = csDestroyedWaiting )
{
m_TicksSinceDestruction + = 1 ; // This field is misused for the timeout counting
if ( m_TicksSinceDestruction > TICKS_BEFORE_CLOSE )
{
m_State = csDestroyed ;
}
return ;
}
2013-08-13 16:45:29 -04:00
// Process received network data:
AString IncomingData ;
{
cCSLock Lock ( m_CSIncomingData ) ;
std : : swap ( IncomingData , m_IncomingData ) ;
}
m_Protocol - > DataReceived ( IncomingData . data ( ) , IncomingData . size ( ) ) ;
2012-11-01 17:38:20 -04:00
m_TimeSinceLastPacket + = a_Dt ;
if ( m_TimeSinceLastPacket > 30000.f ) // 30 seconds time-out
2012-06-14 09:06:06 -04:00
{
2012-08-25 17:59:13 -04:00
SendDisconnect ( " Nooooo!! You timed out! D: Come back! " ) ;
2012-06-14 09:06:06 -04:00
Destroy ( ) ;
}
2013-08-12 02:35:13 -04:00
if ( m_Player = = NULL )
{
return ;
}
2013-09-08 12:36:06 -04:00
// If the chunk the player's in was just sent, spawn the player:
2013-12-15 14:12:40 -05:00
if ( m_HasSentPlayerChunk & & ( m_State = = csDownloadingWorld ) )
2013-09-08 12:36:06 -04:00
{
m_Protocol - > SendPlayerMoveLook ( ) ;
m_State = csPlaying ;
}
2013-07-03 03:47:35 -04:00
// Send a ping packet:
2014-04-15 17:40:06 -04:00
if ( m_State = = csPlaying )
2012-06-14 09:06:06 -04:00
{
2014-04-15 17:40:06 -04:00
cTimer t1 ;
if ( ( m_LastPingTime + cClientHandle : : PING_TIME_MS < = t1 . GetNowTime ( ) ) )
{
m_PingID + + ;
m_PingStartTime = t1 . GetNowTime ( ) ;
m_Protocol - > SendKeepAlive ( m_PingID ) ;
m_LastPingTime = m_PingStartTime ;
}
2012-06-14 09:06:06 -04:00
}
2012-09-25 05:54:36 -04:00
// Handle block break animation:
2013-08-12 02:35:13 -04:00
if ( m_BlockDigAnimStage > - 1 )
2012-09-25 05:54:36 -04:00
{
2013-01-11 23:46:01 -05:00
int lastAnimVal = m_BlockDigAnimStage ;
m_BlockDigAnimStage + = ( int ) ( m_BlockDigAnimSpeed * a_Dt ) ;
if ( m_BlockDigAnimStage > 9000 )
2012-09-25 05:54:36 -04:00
{
2013-01-11 23:46:01 -05:00
m_BlockDigAnimStage = 9000 ;
2012-09-25 05:54:36 -04:00
}
2013-01-11 23:46:01 -05:00
if ( m_BlockDigAnimStage / 1000 ! = lastAnimVal / 1000 )
2012-09-25 05:54:36 -04:00
{
2013-01-11 23:46:01 -05:00
m_Player - > GetWorld ( ) - > BroadcastBlockBreakAnimation ( m_UniqueID , m_BlockDigAnimX , m_BlockDigAnimY , m_BlockDigAnimZ , ( char ) ( m_BlockDigAnimStage / 1000 ) , this ) ;
2012-09-25 05:54:36 -04:00
}
}
2013-06-18 15:32:31 -04:00
2014-03-01 16:24:36 -05:00
// Reset explosion & block change counters:
2014-02-04 18:40:58 -05:00
m_NumExplosionsThisTick = 0 ;
2014-03-01 16:24:36 -05:00
m_NumBlockChangeInteractionsThisTick = 0 ;
2012-06-14 09:06:06 -04:00
}
2013-12-16 04:41:35 -05:00
void cClientHandle : : ServerTick ( float a_Dt )
{
// Handle clients that are waiting for final close while destroyed:
if ( m_State = = csDestroyedWaiting )
{
// Do not wait while the client is not in the world, simply cut them off.
m_State = csDestroyed ;
return ;
}
// Process received network data:
AString IncomingData ;
{
cCSLock Lock ( m_CSIncomingData ) ;
std : : swap ( IncomingData , m_IncomingData ) ;
}
m_Protocol - > DataReceived ( IncomingData . data ( ) , IncomingData . size ( ) ) ;
if ( m_State = = csAuthenticated )
{
StreamChunks ( ) ;
// Remove the client handle from the server, it will be ticked from its cPlayer object from now on
cRoot : : Get ( ) - > GetServer ( ) - > ClientMovedToWorld ( this ) ;
// Add the player to the world (start ticking from there):
m_State = csDownloadingWorld ;
m_Player - > GetWorld ( ) - > AddPlayer ( m_Player ) ;
return ;
}
m_TimeSinceLastPacket + = a_Dt ;
if ( m_TimeSinceLastPacket > 30000.f ) // 30 seconds time-out
{
SendDisconnect ( " Nooooo!! You timed out! D: Come back! " ) ;
Destroy ( ) ;
}
}
2013-07-07 09:06:06 -04:00
void cClientHandle : : SendAttachEntity ( const cEntity & a_Entity , const cEntity * a_Vehicle )
2012-08-17 06:18:07 -04:00
{
2013-07-07 09:06:06 -04:00
m_Protocol - > SendAttachEntity ( a_Entity , a_Vehicle ) ;
2012-08-18 05:56:28 -04:00
}
2013-07-07 09:06:06 -04:00
void cClientHandle : : SendBlockAction ( int a_BlockX , int a_BlockY , int a_BlockZ , char a_Byte1 , char a_Byte2 , BLOCKTYPE a_BlockType )
2012-08-18 05:56:28 -04:00
{
2013-07-07 09:06:06 -04:00
m_Protocol - > SendBlockAction ( a_BlockX , a_BlockY , a_BlockZ , a_Byte1 , a_Byte2 , a_BlockType ) ;
2012-08-18 06:38:15 -04:00
}
2013-07-07 09:06:06 -04:00
void cClientHandle : : SendBlockBreakAnim ( int a_EntityID , int a_BlockX , int a_BlockY , int a_BlockZ , char a_Stage )
2012-08-18 06:38:15 -04:00
{
2013-07-07 09:06:06 -04:00
m_Protocol - > SendBlockBreakAnim ( a_EntityID , a_BlockX , a_BlockY , a_BlockZ , a_Stage ) ;
2012-08-17 06:18:07 -04:00
}
2013-07-07 09:06:06 -04:00
void cClientHandle : : SendBlockChange ( int a_BlockX , int a_BlockY , int a_BlockZ , BLOCKTYPE a_BlockType , NIBBLETYPE a_BlockMeta )
2012-08-19 07:51:17 -04:00
{
2013-07-07 09:06:06 -04:00
m_Protocol - > SendBlockChange ( a_BlockX , a_BlockY , a_BlockZ , a_BlockType , a_BlockMeta ) ;
2012-08-19 07:51:17 -04:00
}
2013-07-07 09:06:06 -04:00
void cClientHandle : : SendBlockChanges ( int a_ChunkX , int a_ChunkZ , const sSetBlockVector & a_Changes )
2012-08-19 07:51:17 -04:00
{
2013-08-19 15:55:03 -04:00
ASSERT ( ! a_Changes . empty ( ) ) ; // We don't want to be sending empty change packets!
2013-07-07 09:06:06 -04:00
m_Protocol - > SendBlockChanges ( a_ChunkX , a_ChunkZ , a_Changes ) ;
2012-08-19 07:51:17 -04:00
}
2014-02-15 17:16:44 -05:00
void cClientHandle : : SendChat ( const AString & a_Message , eMessageType a_ChatPrefix , const AString & a_AdditionalData )
2012-08-19 07:51:17 -04:00
{
2014-04-24 22:14:00 -04:00
bool ShouldAppendChatPrefixes = true ;
2014-02-07 13:58:52 -05:00
if ( GetPlayer ( ) - > GetWorld ( ) = = NULL )
{
cWorld * World = cRoot : : Get ( ) - > GetWorld ( GetPlayer ( ) - > GetLoadedWorldName ( ) ) ;
if ( World = = NULL )
{
World = cRoot : : Get ( ) - > GetDefaultWorld ( ) ;
}
if ( ! World - > ShouldUseChatPrefixes ( ) )
{
ShouldAppendChatPrefixes = false ;
}
}
else if ( ! GetPlayer ( ) - > GetWorld ( ) - > ShouldUseChatPrefixes ( ) )
{
ShouldAppendChatPrefixes = false ;
}
2014-04-24 20:24:39 -04:00
AString Message = FormatMessageType ( ShouldAppendChatPrefixes , a_ChatPrefix , a_AdditionalData ) ;
2014-02-07 13:58:52 -05:00
2014-04-24 20:24:39 -04:00
m_Protocol - > SendChat ( Message . append ( a_Message ) ) ;
2012-08-19 07:51:17 -04:00
}
2014-02-15 17:16:44 -05:00
void cClientHandle : : SendChat ( const cCompositeChat & a_Message )
{
m_Protocol - > SendChat ( a_Message ) ;
}
2013-07-07 09:06:06 -04:00
void cClientHandle : : SendChunkData ( int a_ChunkX , int a_ChunkZ , cChunkDataSerializer & a_Serializer )
2012-08-19 07:51:17 -04:00
{
2013-08-20 15:13:28 -04:00
ASSERT ( m_Player ! = NULL ) ;
2013-07-07 09:06:06 -04:00
// Check chunks being sent, erase them from m_ChunksToSend:
bool Found = false ;
{
cCSLock Lock ( m_CSChunkLists ) ;
for ( cChunkCoordsList : : iterator itr = m_ChunksToSend . begin ( ) ; itr ! = m_ChunksToSend . end ( ) ; + + itr )
{
if ( ( itr - > m_ChunkX = = a_ChunkX ) & & ( itr - > m_ChunkZ = = a_ChunkZ ) )
{
m_ChunksToSend . erase ( itr ) ;
Found = true ;
break ;
}
} // for itr - m_ChunksToSend[]
}
if ( ! Found )
{
// This just sometimes happens. If you have a reliably replicatable situation for this, go ahead and fix it
// It's not a big issue anyway, just means that some chunks may be compressed several times
// 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());
return ;
}
m_Protocol - > SendChunkData ( a_ChunkX , a_ChunkZ , a_Serializer ) ;
2013-09-08 12:36:06 -04:00
// If it is the chunk the player's in, make them spawn (in the tick thread):
if ( ( m_State = = csAuthenticated ) | | ( m_State = = csDownloadingWorld ) )
{
if ( ( a_ChunkX = = m_Player - > GetChunkX ( ) ) & & ( a_ChunkZ = = m_Player - > GetChunkZ ( ) ) )
{
m_HasSentPlayerChunk = true ;
}
}
2012-08-19 07:51:17 -04:00
}
2013-07-07 09:06:06 -04:00
void cClientHandle : : SendCollectPickup ( const cPickup & a_Pickup , const cPlayer & a_Player )
2012-08-19 07:51:17 -04:00
{
2013-07-07 09:06:06 -04:00
m_Protocol - > SendCollectPickup ( a_Pickup , a_Player ) ;
2012-08-19 07:51:17 -04:00
}
2013-07-07 09:06:06 -04:00
void cClientHandle : : SendDestroyEntity ( const cEntity & a_Entity )
2012-08-19 07:51:17 -04:00
{
2013-07-07 09:06:06 -04:00
m_Protocol - > SendDestroyEntity ( a_Entity ) ;
2012-08-19 07:51:17 -04:00
}
2013-07-07 09:06:06 -04:00
void cClientHandle : : SendDisconnect ( const AString & a_Reason )
2012-08-19 15:42:32 -04:00
{
2013-07-07 09:06:06 -04:00
if ( ! m_HasSentDC )
{
2013-07-08 09:00:09 -04:00
LOGD ( " Sending a DC: \" %s \" " , StripColorCodes ( a_Reason ) . c_str ( ) ) ;
2013-07-07 09:06:06 -04:00
m_Protocol - > SendDisconnect ( a_Reason ) ;
m_HasSentDC = true ;
}
2012-08-19 15:42:32 -04:00
}
2013-07-29 15:42:05 -04:00
void cClientHandle : : SendEditSign ( int a_BlockX , int a_BlockY , int a_BlockZ )
{
m_Protocol - > SendEditSign ( a_BlockX , a_BlockY , a_BlockZ ) ;
}
2013-12-14 12:19:56 -05:00
void cClientHandle : : SendEntityEffect ( const cEntity & a_Entity , int a_EffectID , int a_Amplifier , short a_Duration )
{
m_Protocol - > SendEntityEffect ( a_Entity , a_EffectID , a_Amplifier , a_Duration ) ;
}
2013-07-07 09:06:06 -04:00
void cClientHandle : : SendEntityEquipment ( const cEntity & a_Entity , short a_SlotNum , const cItem & a_Item )
2012-08-19 15:42:32 -04:00
{
2013-07-07 09:06:06 -04:00
m_Protocol - > SendEntityEquipment ( a_Entity , a_SlotNum , a_Item ) ;
2012-08-19 15:42:32 -04:00
}
2013-07-07 09:06:06 -04:00
void cClientHandle : : SendEntityHeadLook ( const cEntity & a_Entity )
2012-08-19 15:42:32 -04:00
{
2013-07-07 09:06:06 -04:00
ASSERT ( a_Entity . GetUniqueID ( ) ! = m_Player - > GetUniqueID ( ) ) ; // Must not send for self
m_Protocol - > SendEntityHeadLook ( a_Entity ) ;
2012-08-19 15:42:32 -04:00
}
2013-07-07 09:06:06 -04:00
void cClientHandle : : SendEntityLook ( const cEntity & a_Entity )
2012-08-19 15:42:32 -04:00
{
ASSERT ( a_Entity . GetUniqueID ( ) ! = m_Player - > GetUniqueID ( ) ) ; // Must not send for self
2013-07-07 09:06:06 -04:00
m_Protocol - > SendEntityLook ( a_Entity ) ;
2012-08-19 15:42:32 -04:00
}
2013-07-07 09:06:06 -04:00
void cClientHandle : : SendEntityMetadata ( const cEntity & a_Entity )
2012-08-19 15:42:32 -04:00
{
2013-07-07 09:06:06 -04:00
m_Protocol - > SendEntityMetadata ( a_Entity ) ;
2012-08-19 15:42:32 -04:00
}
2013-07-07 09:06:06 -04:00
void cClientHandle : : SendEntityRelMove ( const cEntity & a_Entity , char a_RelX , char a_RelY , char a_RelZ )
2012-08-19 15:42:32 -04:00
{
ASSERT ( a_Entity . GetUniqueID ( ) ! = m_Player - > GetUniqueID ( ) ) ; // Must not send for self
2013-07-07 09:06:06 -04:00
m_Protocol - > SendEntityRelMove ( a_Entity , a_RelX , a_RelY , a_RelZ ) ;
2012-08-19 15:42:32 -04:00
}
2013-07-07 09:06:06 -04:00
void cClientHandle : : SendEntityRelMoveLook ( const cEntity & a_Entity , char a_RelX , char a_RelY , char a_RelZ )
2013-03-17 22:51:55 -04:00
{
ASSERT ( a_Entity . GetUniqueID ( ) ! = m_Player - > GetUniqueID ( ) ) ; // Must not send for self
2013-07-07 09:06:06 -04:00
m_Protocol - > SendEntityRelMoveLook ( a_Entity , a_RelX , a_RelY , a_RelZ ) ;
}
void cClientHandle : : SendEntityStatus ( const cEntity & a_Entity , char a_Status )
{
m_Protocol - > SendEntityStatus ( a_Entity , a_Status ) ;
2013-03-17 22:51:55 -04:00
}
2013-07-07 09:06:06 -04:00
void cClientHandle : : SendEntityVelocity ( const cEntity & a_Entity )
2012-08-19 15:42:32 -04:00
{
2013-07-07 09:06:06 -04:00
m_Protocol - > SendEntityVelocity ( a_Entity ) ;
2012-08-19 15:42:32 -04:00
}
2013-07-07 09:06:06 -04:00
void cClientHandle : : SendExplosion ( double a_BlockX , double a_BlockY , double a_BlockZ , float a_Radius , const cVector3iArray & a_BlocksAffected , const Vector3d & a_PlayerMotion )
2013-03-03 14:05:11 -05:00
{
2014-02-04 18:40:58 -05:00
if ( m_NumExplosionsThisTick > MAX_EXPLOSIONS_PER_TICK )
2013-07-07 09:06:06 -04:00
{
2014-02-04 18:40:58 -05:00
LOGD ( " Dropped an explosion! " ) ;
2013-07-07 09:06:06 -04:00
return ;
}
// Update the statistics:
2014-04-12 08:16:48 -04:00
m_NumExplosionsThisTick + + ;
2013-07-07 09:06:06 -04:00
m_Protocol - > SendExplosion ( a_BlockX , a_BlockY , a_BlockZ , a_Radius , a_BlocksAffected , a_PlayerMotion ) ;
2013-03-03 14:05:11 -05:00
}
2013-07-07 09:06:06 -04:00
void cClientHandle : : SendGameMode ( eGameMode a_GameMode )
2012-08-19 15:42:32 -04:00
{
2013-07-07 09:06:06 -04:00
m_Protocol - > SendGameMode ( a_GameMode ) ;
2012-08-19 15:42:32 -04:00
}
void cClientHandle : : SendHealth ( void )
{
2012-08-27 13:31:16 -04:00
m_Protocol - > SendHealth ( ) ;
2012-08-19 15:42:32 -04:00
}
2013-07-07 09:06:06 -04:00
void cClientHandle : : SendInventorySlot ( char a_WindowID , short a_SlotNum , const cItem & a_Item )
2012-08-19 15:42:32 -04:00
{
2013-07-07 09:06:06 -04:00
m_Protocol - > SendInventorySlot ( a_WindowID , a_SlotNum , a_Item ) ;
2012-08-19 15:42:32 -04:00
}
2014-02-13 10:13:09 -05:00
void cClientHandle : : SendMapColumn ( int a_ID , int a_X , int a_Y , const Byte * a_Colors , unsigned int a_Length )
{
m_Protocol - > SendMapColumn ( a_ID , a_X , a_Y , a_Colors , a_Length ) ;
}
2014-02-18 13:50:08 -05:00
void cClientHandle : : SendMapDecorators ( int a_ID , const cMapDecoratorList & a_Decorators )
{
m_Protocol - > SendMapDecorators ( a_ID , a_Decorators ) ;
}
2014-02-13 10:13:09 -05:00
void cClientHandle : : SendMapInfo ( int a_ID , unsigned int a_Scale )
{
m_Protocol - > SendMapInfo ( a_ID , a_Scale ) ;
}
2013-12-22 08:45:25 -05:00
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_ParticleAmmount )
{
m_Protocol - > SendParticleEffect ( a_ParticleName , a_SrcX , a_SrcY , a_SrcZ , a_OffsetX , a_OffsetY , a_OffsetZ , a_ParticleData , a_ParticleAmmount ) ;
}
2013-07-07 09:06:06 -04:00
void cClientHandle : : SendPickupSpawn ( const cPickup & a_Pickup )
2012-08-19 15:42:32 -04:00
{
2013-07-07 09:06:06 -04:00
m_Protocol - > SendPickupSpawn ( a_Pickup ) ;
2012-08-19 15:42:32 -04:00
}
2014-02-17 19:16:03 -05:00
void cClientHandle : : SendPaintingSpawn ( const cPainting & a_Painting )
{
m_Protocol - > SendPaintingSpawn ( a_Painting ) ;
}
2012-08-19 15:42:32 -04:00
2013-12-06 18:47:07 -05:00
void cClientHandle : : SendEntityAnimation ( const cEntity & a_Entity , char a_Animation )
2012-08-19 15:42:32 -04:00
{
2013-12-06 18:47:07 -05:00
m_Protocol - > SendEntityAnimation ( a_Entity , a_Animation ) ;
2012-08-19 15:42:32 -04:00
}
2013-12-15 08:48:17 -05:00
void cClientHandle : : SendPlayerAbilities ( )
{
m_Protocol - > SendPlayerAbilities ( ) ;
}
2013-07-07 09:06:06 -04:00
void cClientHandle : : SendPlayerListItem ( const cPlayer & a_Player , bool a_IsOnline )
2012-08-19 17:14:45 -04:00
{
2013-07-07 09:06:06 -04:00
m_Protocol - > SendPlayerListItem ( a_Player , a_IsOnline ) ;
2012-08-19 17:14:45 -04:00
}
2013-07-07 09:06:06 -04:00
void cClientHandle : : SendPlayerMaxSpeed ( void )
2013-04-17 22:42:45 -04:00
{
2013-07-07 09:06:06 -04:00
m_Protocol - > SendPlayerMaxSpeed ( ) ;
2013-04-17 22:42:45 -04:00
}
2013-07-07 09:06:06 -04:00
void cClientHandle : : SendPlayerMoveLook ( void )
2012-08-19 17:14:45 -04:00
{
2013-07-07 09:06:06 -04:00
/*
LOGD ( " Sending PlayerMoveLook: {%0.2f, %0.2f, %0.2f}, stance %0.2f, OnGround: %d " ,
m_Player - > GetPosX ( ) , m_Player - > GetPosY ( ) , m_Player - > GetPosZ ( ) , m_Player - > GetStance ( ) , m_Player - > IsOnGround ( ) ? 1 : 0
) ;
*/
m_Protocol - > SendPlayerMoveLook ( ) ;
2012-08-19 17:14:45 -04:00
}
2013-07-07 09:06:06 -04:00
void cClientHandle : : SendPlayerPosition ( void )
2012-08-19 17:14:45 -04:00
{
2013-07-07 09:06:06 -04:00
m_Protocol - > SendPlayerPosition ( ) ;
2012-08-19 17:14:45 -04:00
}
2012-08-24 03:58:26 -04:00
void cClientHandle : : SendPlayerSpawn ( const cPlayer & a_Player )
{
2012-08-27 13:31:16 -04:00
if ( a_Player . GetUniqueID ( ) = = m_Player - > GetUniqueID ( ) )
{
// Do NOT send this packet to myself
return ;
}
2012-11-16 10:56:41 -05:00
LOGD ( " Spawning player \" %s \" on client \" %s \" @ %s " ,
2012-09-23 12:25:32 -04:00
a_Player . GetName ( ) . c_str ( ) , GetPlayer ( ) - > GetName ( ) . c_str ( ) , GetIPString ( ) . c_str ( )
) ;
2012-08-27 13:31:16 -04:00
m_Protocol - > SendPlayerSpawn ( a_Player ) ;
2012-08-24 03:58:26 -04:00
}
2014-01-09 05:39:42 -05:00
void cClientHandle : : SendPluginMessage ( const AString & a_Channel , const AString & a_Message )
{
m_Protocol - > SendPluginMessage ( a_Channel , a_Message ) ;
}
2013-12-14 12:19:56 -05:00
void cClientHandle : : SendRemoveEntityEffect ( const cEntity & a_Entity , int a_EffectID )
{
m_Protocol - > SendRemoveEntityEffect ( a_Entity , a_EffectID ) ;
}
2014-06-08 15:58:08 -04:00
void cClientHandle : : SendRespawn ( const cWorld & a_World )
2012-08-24 03:58:26 -04:00
{
2014-06-08 15:58:08 -04:00
m_Protocol - > SendRespawn ( a_World ) ;
2012-08-24 03:58:26 -04:00
}
2013-11-15 10:23:50 -05:00
void cClientHandle : : SendExperience ( void )
2013-11-15 06:42:09 -05:00
{
2013-11-15 10:23:50 -05:00
m_Protocol - > SendExperience ( ) ;
2013-11-15 06:42:09 -05:00
}
2013-11-25 15:43:43 -05:00
void cClientHandle : : SendExperienceOrb ( const cExpOrb & a_ExpOrb )
2013-11-25 14:04:39 -05:00
{
2013-11-25 15:43:43 -05:00
m_Protocol - > SendExperienceOrb ( a_ExpOrb ) ;
2013-11-25 14:04:39 -05:00
}
2014-01-21 08:58:17 -05:00
void cClientHandle : : SendScoreboardObjective ( const AString & a_Name , const AString & a_DisplayName , Byte a_Mode )
{
m_Protocol - > SendScoreboardObjective ( a_Name , a_DisplayName , a_Mode ) ;
}
void cClientHandle : : SendScoreUpdate ( const AString & a_Objective , const AString & a_Player , cObjective : : Score a_Score , Byte a_Mode )
{
m_Protocol - > SendScoreUpdate ( a_Objective , a_Player , a_Score , a_Mode ) ;
}
void cClientHandle : : SendDisplayObjective ( const AString & a_Objective , cScoreboard : : eDisplaySlot a_Display )
{
m_Protocol - > SendDisplayObjective ( a_Objective , a_Display ) ;
}
2013-07-07 09:06:06 -04:00
void cClientHandle : : SendSoundEffect ( const AString & a_SoundName , int a_SrcX , int a_SrcY , int a_SrcZ , float a_Volume , float a_Pitch )
2012-12-26 04:12:00 -05:00
{
2013-07-07 09:06:06 -04:00
m_Protocol - > SendSoundEffect ( a_SoundName , a_SrcX , a_SrcY , a_SrcZ , a_Volume , a_Pitch ) ;
2012-12-26 04:12:00 -05:00
}
2013-07-07 09:06:06 -04:00
void cClientHandle : : SendSoundParticleEffect ( int a_EffectID , int a_SrcX , int a_SrcY , int a_SrcZ , int a_Data )
2012-08-24 03:58:26 -04:00
{
2013-07-07 09:06:06 -04:00
m_Protocol - > SendSoundParticleEffect ( a_EffectID , a_SrcX , a_SrcY , a_SrcZ , a_Data ) ;
2012-08-24 03:58:26 -04:00
}
2013-07-07 09:06:06 -04:00
void cClientHandle : : SendSpawnFallingBlock ( const cFallingBlock & a_FallingBlock )
2012-08-24 03:58:26 -04:00
{
2013-07-07 09:06:06 -04:00
m_Protocol - > SendSpawnFallingBlock ( a_FallingBlock ) ;
2012-08-24 03:58:26 -04:00
}
2013-07-07 09:06:06 -04:00
void cClientHandle : : SendSpawnMob ( const cMonster & a_Mob )
2012-08-24 05:49:00 -04:00
{
2013-07-07 09:06:06 -04:00
m_Protocol - > SendSpawnMob ( a_Mob ) ;
2012-08-24 05:49:00 -04:00
}
2013-07-07 09:06:06 -04:00
void cClientHandle : : SendSpawnObject ( const cEntity & a_Entity , char a_ObjectType , int a_ObjectData , Byte a_Yaw , Byte a_Pitch )
2012-08-25 13:52:08 -04:00
{
2013-07-07 09:06:06 -04:00
m_Protocol - > SendSpawnObject ( a_Entity , a_ObjectType , a_ObjectData , a_Yaw , a_Pitch ) ;
2012-08-25 13:52:08 -04:00
}
2013-09-22 14:35:35 -04:00
void cClientHandle : : SendSpawnVehicle ( const cEntity & a_Vehicle , char a_VehicleType , char a_VehicleSubType ) // VehicleSubType is specific to Minecarts
2012-08-25 13:52:08 -04:00
{
2013-08-29 08:47:22 -04:00
m_Protocol - > SendSpawnVehicle ( a_Vehicle , a_VehicleType , a_VehicleSubType ) ;
2012-08-25 13:52:08 -04:00
}
2014-05-11 07:57:06 -04:00
void cClientHandle : : SendStatistics ( const cStatManager & a_Manager )
{
m_Protocol - > SendStatistics ( a_Manager ) ;
}
2013-07-30 16:48:59 -04:00
void cClientHandle : : SendTabCompletionResults ( const AStringVector & a_Results )
{
m_Protocol - > SendTabCompletionResults ( a_Results ) ;
}
2013-07-07 09:06:06 -04:00
void cClientHandle : : SendTeleportEntity ( const cEntity & a_Entity )
2012-08-25 13:52:08 -04:00
{
2013-07-07 09:06:06 -04:00
m_Protocol - > SendTeleportEntity ( a_Entity ) ;
2012-08-25 13:52:08 -04:00
}
2013-07-07 09:06:06 -04:00
void cClientHandle : : SendThunderbolt ( int a_BlockX , int a_BlockY , int a_BlockZ )
2012-08-25 17:46:18 -04:00
{
2013-07-07 09:06:06 -04:00
m_Protocol - > SendThunderbolt ( a_BlockX , a_BlockY , a_BlockZ ) ;
2012-08-25 17:46:18 -04:00
}
2012-11-01 17:38:20 -04:00
void cClientHandle : : SendTimeUpdate ( Int64 a_WorldAge , Int64 a_TimeOfDay )
2012-08-25 17:46:18 -04:00
{
2012-11-01 17:38:20 -04:00
m_Protocol - > SendTimeUpdate ( a_WorldAge , a_TimeOfDay ) ;
2012-08-25 17:46:18 -04:00
}
2013-07-07 09:06:06 -04:00
void cClientHandle : : SendUnloadChunk ( int a_ChunkX , int a_ChunkZ )
2012-08-25 17:46:18 -04:00
{
2013-07-07 09:06:06 -04:00
m_Protocol - > SendUnloadChunk ( a_ChunkX , a_ChunkZ ) ;
2012-08-25 17:46:18 -04:00
}
2014-01-19 14:42:25 -05:00
void cClientHandle : : SendUpdateBlockEntity ( cBlockEntity & a_BlockEntity )
2014-01-18 19:54:38 -05:00
{
2014-01-19 14:42:25 -05:00
m_Protocol - > SendUpdateBlockEntity ( a_BlockEntity ) ;
2014-01-18 19:54:38 -05:00
}
2012-08-25 17:46:18 -04:00
2013-07-07 09:06:06 -04:00
void cClientHandle : : 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
)
2012-09-11 08:01:34 -04:00
{
2013-07-07 09:06:06 -04:00
m_Protocol - > SendUpdateSign (
a_BlockX , a_BlockY , a_BlockZ ,
a_Line1 , a_Line2 , a_Line3 , a_Line4
) ;
2012-09-11 08:01:34 -04:00
}
2013-07-07 09:06:06 -04:00
void cClientHandle : : SendUseBed ( const cEntity & a_Entity , int a_BlockX , int a_BlockY , int a_BlockZ )
2012-10-21 03:46:28 -04:00
{
2013-07-07 09:06:06 -04:00
m_Protocol - > SendUseBed ( a_Entity , a_BlockX , a_BlockY , a_BlockZ ) ;
2012-10-21 03:46:28 -04:00
}
2013-07-07 09:06:06 -04:00
void cClientHandle : : SendWeather ( eWeather a_Weather )
2012-09-25 05:54:36 -04:00
{
2013-07-07 09:06:06 -04:00
m_Protocol - > SendWeather ( a_Weather ) ;
2012-09-25 05:54:36 -04:00
}
2013-08-18 07:26:37 -04:00
void cClientHandle : : SendWholeInventory ( const cWindow & a_Window )
2012-09-30 12:37:44 -04:00
{
2013-08-18 07:26:37 -04:00
m_Protocol - > SendWholeInventory ( a_Window ) ;
2012-09-30 12:37:44 -04:00
}
2012-09-29 16:43:42 -04:00
2013-08-18 07:26:37 -04:00
void cClientHandle : : SendWindowClose ( const cWindow & a_Window )
2013-02-18 11:48:50 -05:00
{
2013-08-18 07:26:37 -04:00
m_Protocol - > SendWindowClose ( a_Window ) ;
2013-02-18 11:48:50 -05:00
}
2013-11-08 15:32:14 -05:00
void cClientHandle : : SendWindowOpen ( const cWindow & a_Window )
2012-08-26 17:01:07 -04:00
{
2013-11-08 15:32:14 -05:00
m_Protocol - > SendWindowOpen ( a_Window ) ;
2013-08-18 07:26:37 -04:00
}
void cClientHandle : : SendWindowProperty ( const cWindow & a_Window , int a_Property , int a_Value )
{
m_Protocol - > SendWindowProperty ( a_Window , a_Property , a_Value ) ;
2012-08-26 17:01:07 -04:00
}
2012-06-14 09:06:06 -04:00
const AString & cClientHandle : : GetUsername ( void ) const
{
return m_Username ;
}
2012-10-18 17:54:56 -04:00
void cClientHandle : : SetUsername ( const AString & a_Username )
{
m_Username = a_Username ;
}
2012-06-14 09:06:06 -04:00
void cClientHandle : : SetViewDistance ( int a_ViewDistance )
{
if ( a_ViewDistance < MIN_VIEW_DISTANCE )
{
a_ViewDistance = MIN_VIEW_DISTANCE ;
}
if ( a_ViewDistance > MAX_VIEW_DISTANCE )
{
a_ViewDistance = MAX_VIEW_DISTANCE ;
}
m_ViewDistance = a_ViewDistance ;
// Need to re-stream chunks for the change to become apparent:
StreamChunks ( ) ;
}
2014-02-20 17:24:39 -05:00
bool cClientHandle : : HasPluginChannel ( const AString & a_PluginChannel )
{
return ( m_PluginChannels . find ( a_PluginChannel ) ! = m_PluginChannels . end ( ) ) ;
}
2012-06-14 09:06:06 -04:00
bool cClientHandle : : WantsSendChunk ( int a_ChunkX , int a_ChunkY , int a_ChunkZ )
{
2012-11-11 09:00:58 -05:00
if ( m_State > = csDestroying )
{
return false ;
}
2012-06-14 09:06:06 -04:00
cCSLock Lock ( m_CSChunkLists ) ;
return ( std : : find ( m_ChunksToSend . begin ( ) , m_ChunksToSend . end ( ) , cChunkCoords ( a_ChunkX , a_ChunkY , a_ChunkZ ) ) ! = m_ChunksToSend . end ( ) ) ;
}
void cClientHandle : : AddWantedChunk ( int a_ChunkX , int a_ChunkZ )
{
2012-11-11 09:00:58 -05:00
if ( m_State > = csDestroying )
{
return ;
}
2012-06-14 09:06:06 -04:00
LOGD ( " Adding chunk [%d, %d] to wanted chunks for client %p " , a_ChunkX , a_ChunkZ , this ) ;
cCSLock Lock ( m_CSChunkLists ) ;
if ( std : : find ( m_ChunksToSend . begin ( ) , m_ChunksToSend . end ( ) , cChunkCoords ( a_ChunkX , ZERO_CHUNK_Y , a_ChunkZ ) ) = = m_ChunksToSend . end ( ) )
{
m_ChunksToSend . push_back ( cChunkCoords ( a_ChunkX , ZERO_CHUNK_Y , a_ChunkZ ) ) ;
}
}
2012-08-27 13:31:16 -04:00
void cClientHandle : : PacketBufferFull ( void )
2012-06-14 09:06:06 -04:00
{
2012-08-27 13:31:16 -04:00
// Too much data in the incoming queue, the server is probably too busy, kick the client:
2012-09-25 04:23:19 -04:00
LOGERROR ( " Too much data in queue for client \" %s \" @ %s, kicking them. " , m_Username . c_str ( ) , m_IPString . c_str ( ) ) ;
2012-08-27 13:31:16 -04:00
SendDisconnect ( " Server busy " ) ;
Destroy ( ) ;
}
2012-06-14 09:06:06 -04:00
2012-08-27 13:31:16 -04:00
2013-12-12 17:05:23 -05:00
void cClientHandle : : PacketUnknown ( UInt32 a_PacketType )
2012-08-27 13:31:16 -04:00
{
2013-12-12 17:05:23 -05:00
LOGERROR ( " Unknown packet type 0x%x from client \" %s \" @ %s " , a_PacketType , m_Username . c_str ( ) , m_IPString . c_str ( ) ) ;
2012-08-27 13:31:16 -04:00
AString Reason ;
2013-12-12 17:05:23 -05:00
Printf ( Reason , " Unknown [C->S] PacketType: 0x%x " , a_PacketType ) ;
2012-08-27 13:31:16 -04:00
SendDisconnect ( Reason ) ;
Destroy ( ) ;
2012-06-14 09:06:06 -04:00
}
2012-08-27 13:31:16 -04:00
void cClientHandle : : PacketError ( unsigned char a_PacketType )
2012-06-14 09:06:06 -04:00
{
2012-08-27 13:31:16 -04:00
LOGERROR ( " Protocol error while parsing packet type 0x%02x; disconnecting client \" %s \" " , a_PacketType , m_Username . c_str ( ) ) ;
SendDisconnect ( " Protocol error " ) ;
Destroy ( ) ;
}
2012-06-14 09:06:06 -04:00
2012-08-27 13:31:16 -04:00
2014-05-01 09:08:15 -04:00
bool cClientHandle : : DataReceived ( const char * a_Data , size_t a_Size )
2012-08-27 13:31:16 -04:00
{
2013-08-13 16:45:29 -04:00
// Data is received from the client, store it in the buffer to be processed by the Tick thread:
2012-11-01 17:38:20 -04:00
m_TimeSinceLastPacket = 0 ;
2013-08-13 16:45:29 -04:00
cCSLock Lock ( m_CSIncomingData ) ;
m_IncomingData . append ( a_Data , a_Size ) ;
2014-05-01 09:08:15 -04:00
return false ;
2012-08-27 13:31:16 -04:00
}
void cClientHandle : : GetOutgoingData ( AString & a_Data )
{
// Data can be sent to client
2012-09-04 15:05:35 -04:00
{
cCSLock Lock ( m_CSOutgoingData ) ;
m_OutgoingData . ReadAll ( a_Data ) ;
m_OutgoingData . CommitRead ( ) ;
2012-09-06 04:33:43 -04:00
a_Data . append ( m_OutgoingDataOverflow ) ;
m_OutgoingDataOverflow . clear ( ) ;
2012-09-04 15:05:35 -04:00
}
2012-06-14 09:06:06 -04:00
// Disconnect player after all packets have been sent
2013-01-05 14:05:15 -05:00
if ( m_HasSentDC & & a_Data . empty ( ) )
2012-06-14 09:06:06 -04:00
{
Destroy ( ) ;
}
}
void cClientHandle : : SocketClosed ( void )
{
// The socket has been closed for any reason
2013-12-18 13:17:17 -05:00
LOGD ( " Player %s @ %s disconnected " , m_Username . c_str ( ) , m_IPString . c_str ( ) ) ;
2014-05-02 05:18:02 -04:00
if ( ! m_Username . empty ( ) ) // Ignore client pings
2013-12-18 13:17:17 -05:00
{
2014-05-02 05:18:02 -04:00
cRoot : : Get ( ) - > GetPluginManager ( ) - > CallHookDisconnect ( * this , " Player disconnected " ) ;
2013-12-18 13:17:17 -05:00
}
2012-09-25 04:23:19 -04:00
Destroy ( ) ;
2012-06-14 09:06:06 -04:00
}
2012-06-14 16:06:25 -04:00
2014-04-16 08:33:03 -04:00
void cClientHandle : : HandleEnchantItem ( Byte & WindowID , Byte & Enchantment )
{
cEnchantingWindow * Window = ( cEnchantingWindow * ) m_Player - > GetWindow ( ) ;
cItem Item = * Window - > m_SlotArea - > GetSlot ( 0 , * m_Player ) ;
int BaseEnchantmentLevel = Window - > GetPropertyValue ( Enchantment ) ;
2014-04-17 13:31:43 -04:00
if ( Item . EnchantByXPLevels ( BaseEnchantmentLevel ) )
2014-04-16 08:33:03 -04:00
{
2014-04-18 08:10:31 -04:00
if ( m_Player - > IsGameModeCreative ( ) | | m_Player - > DeltaExperience ( - m_Player - > XpForLevel ( BaseEnchantmentLevel ) ) > = 0 )
2014-04-17 13:31:43 -04:00
{
Window - > m_SlotArea - > SetSlot ( 0 , * m_Player , Item ) ;
Window - > SendSlot ( * m_Player , Window - > m_SlotArea , 0 ) ;
Window - > BroadcastWholeWindow ( ) ;
2014-04-16 08:33:03 -04:00
2014-04-17 13:31:43 -04:00
Window - > SetProperty ( 0 , 0 , * m_Player ) ;
Window - > SetProperty ( 1 , 0 , * m_Player ) ;
Window - > SetProperty ( 2 , 0 , * m_Player ) ;
}
2014-04-16 08:33:03 -04:00
}
}