2012-06-14 13:06:06 +00:00
# include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
2012-09-23 22:09:57 +00:00
# include "ClientHandle.h"
# include "Server.h"
# include "World.h"
2013-08-19 09:39:13 +00:00
# include "Entities/Pickup.h"
2013-12-08 11:17:54 +00:00
# include "Bindings/PluginManager.h"
2013-08-19 09:39:13 +00:00
# include "Entities/Player.h"
2012-09-23 22:09:57 +00:00
# include "Inventory.h"
2013-05-28 19:12:47 +00:00
# include "BlockEntities/ChestEntity.h"
2014-01-18 17:58:46 +00:00
# include "BlockEntities/CommandBlockEntity.h"
2013-05-28 19:12:47 +00:00
# include "BlockEntities/SignEntity.h"
2012-09-23 17:19:34 +00:00
# include "UI/Window.h"
2012-09-23 22:09:57 +00:00
# include "Item.h"
2012-09-23 20:53:08 +00:00
# include "Mobs/Monster.h"
2012-09-23 22:09:57 +00:00
# include "ChatColor.h"
2012-09-23 21:23:33 +00:00
# include "OSSupport/Socket.h"
# include "OSSupport/Timer.h"
2012-09-29 13:59:32 +00:00
# include "Items/ItemHandler.h"
# include "Blocks/BlockHandler.h"
2013-08-25 14:11:19 +00:00
# include "Blocks/BlockSlab.h"
2014-01-26 14:20:39 +00:00
# include "Blocks/ChunkInterface.h"
2012-06-14 13:06:06 +00:00
2012-09-23 22:09:57 +00:00
# include "Root.h"
2012-06-14 13:06:06 +00:00
2014-04-13 11:04:56 +00:00
# include "Protocol/Authenticator.h"
2012-06-14 13:06:06 +00:00
# include "MersenneTwister.h"
2012-09-23 20:03:26 +00:00
# include "Protocol/ProtocolRecognizer.h"
2014-02-15 22:16:44 +00:00
# include "CompositeChat.h"
2014-02-23 19:02:44 +00:00
# include "Items/ItemSword.h"
2012-06-14 13:06:06 +00:00
2014-06-27 17:34:53 +00:00
# include "polarssl/md5.h"
2014-04-14 18:21:00 +00:00
2012-06-14 13:06:06 +00:00
2014-02-04 23:40:58 +00:00
/** Maximum number of explosions to send this tick, server will start dropping if exceeded */
2014-02-05 00:45:08 +00:00
# define MAX_EXPLOSIONS_PER_TICK 20
2013-06-18 19:32:31 +00:00
2014-03-01 21:24:36 +00:00
/** Maximum number of block change interactions a player can perform per tick - exceeding this causes a kick */
# define MAX_BLOCK_CHANGE_INTERACTIONS 20
2012-06-14 13:06:06 +00: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 ;
2014-07-17 20:15:34 +00:00
////////////////////////////////////////////////////////////////////////////////
2012-06-14 13:06:06 +00:00
// cClientHandle:
2013-12-20 18:10:07 +00: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_State ( csConnected ) ,
m_ShouldCheckDownloaded ( false ) ,
2014-02-04 23:40:58 +00:00
m_NumExplosionsThisTick ( 0 ) ,
2013-12-20 18:10:07 +00:00
m_UniqueID ( 0 ) ,
2014-03-08 16:33:38 +00:00
m_HasSentPlayerChunk ( false ) ,
m_Locale ( " en_GB " )
2012-06-14 13:06:06 +00:00
{
2012-08-30 09:56:59 +00:00
m_Protocol = new cProtocolRecognizer ( this ) ;
2012-08-27 17:31:16 +00:00
2014-07-17 14:33:09 +00:00
s_ClientCount + + ; // Not protected by CS because clients are always constructed from the same thread
2012-06-14 13:06:06 +00:00
m_UniqueID = s_ClientCount ;
cTimer t1 ;
m_LastPingTime = t1 . GetNowTime ( ) ;
2012-08-27 17:31:16 +00:00
LOGD ( " New ClientHandle created at %p " , this ) ;
2012-06-14 13:06:06 +00:00
}
cClientHandle : : ~ cClientHandle ( )
{
2014-07-12 23:12:32 +00:00
ASSERT ( m_State = = csDestroyed ) ; // Has Destroy() been called?
2012-11-11 14:00:58 +00:00
2012-08-27 17:31:16 +00:00
LOGD ( " Deleting client \" %s \" at %p " , GetUsername ( ) . c_str ( ) , this ) ;
2012-06-14 13:06:06 +00: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 17:31:16 +00:00
World - > BroadcastPlayerListItem ( * m_Player , false , this ) ;
2012-06-14 13:06:06 +00:00
}
if ( World ! = NULL )
{
2013-04-13 21:02:10 +00:00
m_Player - > Destroy ( ) ;
2012-06-14 13:06:06 +00:00
}
2013-04-13 21:02:10 +00:00
delete m_Player ;
m_Player = NULL ;
2012-06-14 13:06:06 +00:00
}
2013-01-05 19:05:15 +00:00
if ( ! m_HasSentDC )
2012-06-14 13:06:06 +00:00
{
2012-09-25 08:23:19 +00:00
SendDisconnect ( " Server shut down? Kthnxbai " ) ;
2012-06-14 13:06:06 +00:00
}
2014-01-19 18:31:43 +00:00
// Close the socket as soon as it sends all outgoing data:
2012-09-25 08:23:19 +00:00
cRoot : : Get ( ) - > GetServer ( ) - > RemoveClient ( this ) ;
2012-06-14 13:06:06 +00:00
2012-08-27 17:31:16 +00:00
delete m_Protocol ;
m_Protocol = NULL ;
LOGD ( " ClientHandle at %p deleted " , this ) ;
2012-06-14 13:06:06 +00:00
}
2013-08-13 20:45:29 +00:00
void cClientHandle : : Destroy ( void )
2012-06-14 13:06:06 +00:00
{
2013-08-13 20:45:29 +00: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 13:06:06 +00:00
if ( ( m_Player ! = NULL ) & & ( m_Player - > GetWorld ( ) ! = NULL ) )
{
RemoveFromAllChunks ( ) ;
m_Player - > GetWorld ( ) - > RemoveClientFromChunkSender ( this ) ;
}
2014-07-12 23:12:32 +00:00
m_State = csDestroyed ;
2012-06-14 13:06:06 +00:00
}
2014-04-14 18:21:00 +00:00
void cClientHandle : : GenerateOfflineUUID ( void )
2014-04-14 20:52:59 +00:00
{
m_UUID = GenerateOfflineUUID ( m_Username ) ;
}
2014-04-26 16:21:49 +00:00
AString cClientHandle : : FormatChatPrefix ( bool ShouldAppendChatPrefixes , AString a_ChatPrefixS , AString m_Color1 , AString m_Color2 )
{
if ( ShouldAppendChatPrefixes )
2014-04-26 21:01:48 +00:00
return Printf ( " %s[%s] %s " , m_Color1 . c_str ( ) , a_ChatPrefixS . c_str ( ) , m_Color2 . c_str ( ) ) ;
2014-04-26 16:21:49 +00:00
else
return Printf ( " %s " , m_Color1 . c_str ( ) ) ;
}
2014-04-25 00:24:39 +00:00
AString cClientHandle : : FormatMessageType ( bool ShouldAppendChatPrefixes , eMessageType a_ChatPrefix , const AString & a_AdditionalData )
{
switch ( a_ChatPrefix )
{
2014-04-27 16:35:41 +00:00
case mtCustom : return " " ;
2014-04-26 21:01:48 +00: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-25 00:24:39 +00:00
case mtPrivateMessage :
{
if ( ShouldAppendChatPrefixes )
2014-04-28 18:37:22 +00:00
{
2014-04-25 00:24:39 +00: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 18:37:22 +00:00
}
2014-04-25 00:24:39 +00:00
else
2014-04-28 18:37:22 +00:00
{
2014-04-25 00:24:39 +00:00
return Printf ( " %s: %s " , a_AdditionalData . c_str ( ) , cChatColor : : LightBlue . c_str ( ) ) ;
2014-04-28 18:37:22 +00:00
}
2014-04-25 00:24:39 +00:00
}
}
ASSERT ( ! " Unhandled chat prefix type! " ) ;
2014-04-27 16:35:41 +00:00
return " " ;
2014-04-25 00:24:39 +00:00
}
2014-04-14 20:52:59 +00:00
AString cClientHandle : : GenerateOfflineUUID ( const AString & a_Username )
2014-04-14 18:21:00 +00:00
{
2014-06-21 20:13:35 +00:00
// Online UUIDs are always version 4 (random)
// We use Version 3 (MD5 hash) UUIDs for the offline UUIDs
// This guarantees that they will never collide with an online UUID and can be distinguished.
2014-04-14 18:21:00 +00: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-06-27 17:34:53 +00:00
unsigned char MD5 [ 16 ] ;
md5 ( ( const unsigned char * ) a_Username . c_str ( ) , a_Username . length ( ) , MD5 ) ;
2014-06-29 16:27:41 +00:00
MD5 [ 6 ] & = 0x0f ; // Need to trim to 4 bits only...
MD5 [ 8 ] & = 0x0f ; // ... otherwise %01x overflows into two chars
2014-06-27 17:34:53 +00:00
return Printf ( " %02x%02x%02x%02x-%02x%02x-3%01x%02x-8%01x%02x-%02x%02x%02x%02x%02x%02x " ,
2014-06-29 16:27:41 +00:00
MD5 [ 0 ] , MD5 [ 1 ] , MD5 [ 2 ] , MD5 [ 3 ] ,
MD5 [ 4 ] , MD5 [ 5 ] , MD5 [ 6 ] , MD5 [ 7 ] ,
2014-06-27 17:34:53 +00:00
MD5 [ 8 ] , MD5 [ 9 ] , MD5 [ 10 ] , MD5 [ 11 ] ,
MD5 [ 12 ] , MD5 [ 13 ] , MD5 [ 14 ] , MD5 [ 15 ]
) ;
2014-04-14 18:21:00 +00:00
}
2014-06-21 20:13:35 +00:00
bool cClientHandle : : IsUUIDOnline ( const AString & a_UUID )
{
// Online UUIDs are always version 4 (random)
// We use Version 3 (MD5 hash) UUIDs for the offline UUIDs
// This guarantees that they will never collide with an online UUID and can be distinguished.
// The version-specifying char is at pos #12 of raw UUID, pos #14 in dashed-UUID.
switch ( a_UUID . size ( ) )
{
case 32 :
{
// This is the UUID format without dashes, the version char is at pos #12:
return ( a_UUID [ 12 ] = = ' 4 ' ) ;
}
case 36 :
{
// This is the UUID format with dashes, the version char is at pos #14:
return ( a_UUID [ 14 ] = = ' 4 ' ) ;
}
}
return false ;
}
2012-06-14 13:06:06 +00:00
void cClientHandle : : Kick ( const AString & a_Reason )
{
if ( m_State > = csAuthenticating ) // Don't log pings
{
2013-12-07 14:41:58 +00:00
LOGINFO ( " Kicking player %s for \" %s \" " , m_Username . c_str ( ) , StripColorCodes ( a_Reason ) . c_str ( ) ) ;
2012-06-14 13:06:06 +00:00
}
2012-08-25 21:59:13 +00:00
SendDisconnect ( a_Reason ) ;
2012-06-14 13:06:06 +00:00
}
2014-07-15 23:03:47 +00:00
void cClientHandle : : Authenticate ( const AString & a_Name , const AString & a_UUID , const Json : : Value & a_Properties )
2012-06-14 13:06:06 +00:00
{
if ( m_State ! = csAuthenticating )
{
return ;
}
2014-07-14 18:49:31 +00:00
ASSERT ( m_Player = = NULL ) ;
2012-06-14 13:06:06 +00:00
2014-04-13 11:04:56 +00:00
m_Username = a_Name ;
m_UUID = a_UUID ;
2014-07-14 18:49:31 +00:00
m_Properties = a_Properties ;
2014-04-14 20:52:59 +00:00
// Send login success (if the protocol supports it):
m_Protocol - > SendLoginSuccess ( ) ;
2014-04-13 11:04:56 +00:00
2012-06-14 13:06:06 +00: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 17:31:16 +00:00
if ( m_Player - > GetGameMode ( ) = = eGameMode_NotSet )
{
2012-07-13 15:26:27 +00:00
m_Player - > LoginSetGameMode ( World - > GetGameMode ( ) ) ;
2012-08-27 17:31:16 +00:00
}
2012-06-14 13:06:06 +00:00
2012-09-25 08:23:19 +00:00
m_Player - > SetIP ( m_IPString ) ;
2012-06-14 13:06:06 +00:00
2014-02-03 21:12:44 +00:00
if ( ! cRoot : : Get ( ) - > GetPluginManager ( ) - > CallHookPlayerJoined ( * m_Player ) )
{
2014-02-05 23:24:16 +00:00
cRoot : : Get ( ) - > BroadcastChatJoin ( Printf ( " %s has joined the game " , GetUsername ( ) . c_str ( ) ) ) ;
2014-07-14 18:49:31 +00:00
LOGINFO ( " Player %s has joined the game " , m_Username . c_str ( ) ) ;
2014-02-03 21:12:44 +00:00
}
2012-09-23 16:54:03 +00:00
m_ConfirmPosition = m_Player - > GetPosition ( ) ;
2012-06-14 13:06:06 +00:00
// Return a server login packet
2012-09-02 12:09:58 +00:00
m_Protocol - > SendLogin ( * m_Player , * World ) ;
2012-06-14 13:06:06 +00:00
// Send Weather if raining:
if ( ( World - > GetWeather ( ) = = 1 ) | | ( World - > GetWeather ( ) = = 2 ) )
{
2012-08-27 17:31:16 +00:00
m_Protocol - > SendWeather ( World - > GetWeather ( ) ) ;
2012-06-14 13:06:06 +00:00
}
// Send time
2012-11-01 21:38:20 +00:00
m_Protocol - > SendTimeUpdate ( World - > GetWorldAge ( ) , World - > GetTimeOfDay ( ) ) ;
2012-06-14 13:06:06 +00:00
2013-11-05 17:36:58 +00:00
// Send contents of the inventory window
m_Protocol - > SendWholeInventory ( * m_Player - > GetWindow ( ) ) ;
2012-06-14 13:06:06 +00:00
// Send health
2012-07-17 12:02:03 +00:00
m_Player - > SendHealth ( ) ;
2013-11-15 15:23:50 +00:00
// Send experience
m_Player - > SendExperience ( ) ;
2012-06-14 13:06:06 +00:00
2014-06-08 19:58:08 +00:00
m_Player - > Initialize ( * World ) ;
2013-08-13 20:45:29 +00:00
m_State = csAuthenticated ;
2014-01-21 13:58:17 +00:00
// Query player team
m_Player - > UpdateTeam ( ) ;
2014-01-21 17:43:13 +00:00
// Send scoreboard data
World - > GetScoreBoard ( ) . SendTo ( * this ) ;
2014-05-11 11:57:06 +00:00
2014-06-28 23:40:15 +00:00
// Send statistics
SendStatistics ( m_Player - > GetStatManager ( ) ) ;
2014-04-29 21:10:50 +00: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 17:43:13 +00:00
2013-01-12 04:46:01 +00:00
cRoot : : Get ( ) - > GetPluginManager ( ) - > CallHookPlayerSpawned ( * m_Player ) ;
2012-06-14 13:06:06 +00:00
}
void cClientHandle : : StreamChunks ( void )
{
2013-08-14 08:24:34 +00:00
if ( ( m_State < csAuthenticated ) | | ( m_State > = csDestroying ) )
2012-06-14 13:06:06 +00:00
{
return ;
}
ASSERT ( m_Player ! = NULL ) ;
2014-06-08 19:58:08 +00:00
2013-02-27 10:01:20 +00: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 13:06:06 +00: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 21:02:10 +00:00
World - > RemoveChunkClient ( itr - > m_ChunkX , itr - > m_ChunkZ , this ) ;
2012-08-27 17:31:16 +00:00
m_Protocol - > SendUnloadChunk ( itr - > m_ChunkX , itr - > m_ChunkZ ) ;
2012-06-14 13:06:06 +00: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 21:02:10 +00:00
StreamChunk ( ChunkPosX + d , ChunkPosZ + i ) ;
StreamChunk ( ChunkPosX - d , ChunkPosZ + i ) ;
2012-06-14 13:06:06 +00:00
} // for i
for ( int i = - d + 1 ; i < d ; + + i )
{
2013-04-13 21:02:10 +00:00
StreamChunk ( ChunkPosX + i , ChunkPosZ + d ) ;
StreamChunk ( ChunkPosX + i , ChunkPosZ - d ) ;
2012-06-14 13:06:06 +00: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 21:02:10 +00:00
void cClientHandle : : StreamChunk ( int a_ChunkX , int a_ChunkZ )
2012-06-14 13:06:06 +00:00
{
2012-11-11 14:00:58 +00:00
if ( m_State > = csDestroying )
{
// Don't stream chunks to clients that are being destroyed
return ;
}
2012-06-14 13:06:06 +00:00
cWorld * World = m_Player - > GetWorld ( ) ;
ASSERT ( World ! = NULL ) ;
2013-04-13 21:02:10 +00:00
if ( World - > AddChunkClient ( a_ChunkX , a_ChunkZ , this ) )
2012-06-14 13:06:06 +00:00
{
{
cCSLock Lock ( m_CSChunkLists ) ;
2013-04-13 21:02:10 +00: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 13:06:06 +00:00
}
2013-04-13 21:02:10 +00:00
World - > SendChunkTo ( a_ChunkX , a_ChunkZ , this ) ;
2012-06-14 13:06:06 +00: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 07:47:35 +00: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 13:06:06 +00:00
}
}
void cClientHandle : : HandlePing ( void )
{
// Somebody tries to retrieve information about the server
AString Reply ;
2014-04-19 15:53:02 +00:00
const cServer & Server = * cRoot : : Get ( ) - > GetServer ( ) ;
2014-04-18 19:09:44 +00:00
2014-07-17 14:33:09 +00:00
Printf ( Reply , " %s%s%i%s%i " ,
2014-04-19 15:53:02 +00:00
Server . GetDescription ( ) . c_str ( ) ,
2013-08-11 17:18:06 +00:00
cChatColor : : Delimiter . c_str ( ) ,
2014-04-19 15:53:02 +00:00
Server . GetNumPlayers ( ) ,
2013-08-11 17:18:06 +00:00
cChatColor : : Delimiter . c_str ( ) ,
2014-04-19 15:53:02 +00:00
Server . GetMaxPlayers ( )
2012-06-14 13:06:06 +00:00
) ;
2014-04-18 19:09:44 +00:00
Kick ( Reply ) ;
2012-06-14 13:06:06 +00:00
}
2012-08-30 08:19:19 +00:00
bool cClientHandle : : HandleLogin ( int a_ProtocolVersion , const AString & a_Username )
2012-06-14 13:06:06 +00:00
{
2012-08-18 09:56:28 +00:00
LOGD ( " LOGIN %s " , a_Username . c_str ( ) ) ;
2012-08-30 08:19:19 +00:00
m_Username = a_Username ;
2012-06-14 13:06:06 +00:00
2012-08-18 09:56:28 +00:00
if ( cRoot : : Get ( ) - > GetPluginManager ( ) - > CallHookLogin ( this , a_ProtocolVersion , a_Username ) )
2012-06-14 13:06:06 +00:00
{
Destroy ( ) ;
2012-08-30 08:19:19 +00:00
return false ;
2012-06-14 13:06:06 +00:00
}
// Schedule for authentication; until then, let them wait (but do not block)
m_State = csAuthenticating ;
2012-09-06 17:36:59 +00:00
cRoot : : Get ( ) - > GetAuthenticator ( ) . Authenticate ( GetUniqueID ( ) , GetUsername ( ) , m_Protocol - > GetAuthServerID ( ) ) ;
2012-08-30 08:19:19 +00:00
return true ;
2012-06-14 13:06:06 +00:00
}
2012-08-18 09:56:28 +00:00
void cClientHandle : : HandleCreativeInventory ( short a_SlotNum , const cItem & a_HeldItem )
2012-06-14 13:06:06 +00:00
{
// This is for creative Inventory changes
2013-10-29 20:19:06 +00:00
if ( ! m_Player - > IsGameModeCreative ( ) )
2012-06-14 13:06:06 +00:00
{
2012-09-20 13:25:54 +00:00
LOGWARNING ( " Got a CreativeInventoryAction packet from user \" %s \" while not in creative mode. Ignoring. " , m_Username . c_str ( ) ) ;
return ;
2012-06-14 13:06:06 +00:00
}
2013-10-28 12:30:24 +00:00
if ( m_Player - > GetWindow ( ) - > GetWindowType ( ) ! = cWindow : : wtInventory )
2012-06-14 13:06:06 +00:00
{
2012-09-20 13:25:54 +00:00
LOGWARNING ( " Got a CreativeInventoryAction packet from user \" %s \" while not in the inventory window. Ignoring. " , m_Username . c_str ( ) ) ;
return ;
2012-06-14 13:06:06 +00:00
}
2012-09-20 13:25:54 +00:00
2013-05-08 09:45:07 +00:00
m_Player - > GetWindow ( ) - > Clicked ( * m_Player , 0 , a_SlotNum , ( a_SlotNum > = 0 ) ? caLeftClick : caLeftClickOutside , a_HeldItem ) ;
2012-06-14 13:06:06 +00:00
}
2013-12-15 14:11:59 +00:00
void cClientHandle : : HandlePlayerAbilities ( bool a_CanFly , bool a_IsFlying , float FlyingSpeed , float WalkingSpeed )
2013-12-15 13:48:17 +00:00
{
2014-07-17 17:13:23 +00:00
UNUSED ( FlyingSpeed ) ; // Ignore the client values for these
2013-12-19 20:53:47 +00:00
UNUSED ( WalkingSpeed ) ;
2013-12-15 14:11:59 +00:00
m_Player - > SetCanFly ( a_CanFly ) ;
m_Player - > SetFlying ( a_IsFlying ) ;
2013-12-15 13:48:17 +00:00
}
2012-08-18 09:56:28 +00:00
void cClientHandle : : HandlePlayerPos ( double a_PosX , double a_PosY , double a_PosZ , double a_Stance , bool a_IsOnGround )
2012-06-14 13:06:06 +00:00
{
2013-09-08 16:36:06 +00: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 14:38:14 +00:00
/*
// TODO: Invalid stance check
if ( ( a_PosY > = a_Stance ) | | ( a_Stance > a_PosY + 1.65 ) )
2012-08-19 19:42:32 +00:00
{
2014-05-26 14:38:14 +00:00
LOGD ( " Invalid stance " ) ;
SendPlayerMoveLook ( ) ;
2014-05-16 20:04:19 +00:00
return ;
}
2014-05-26 14:38:14 +00:00
*/
2013-09-08 16:36:06 +00:00
// If the player has moved too far, "repair" them:
2012-08-19 19:42:32 +00: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 20:01:25 +00:00
// If a jump just started, process food exhaustion:
if ( ( a_PosY > m_Player - > GetPosY ( ) ) & & ! a_IsOnGround & & m_Player - > IsOnGround ( ) )
{
2013-08-08 15:57:34 +00:00
// we only add this exhaustion if the player is not swimming - otherwise we end up with both jump + swim exhaustion
2013-08-09 07:37:39 +00:00
2013-09-08 16:36:06 +00:00
if ( ! m_Player - > IsSwimming ( ) )
2013-08-08 15:57:34 +00:00
{
m_Player - > AddFoodExhaustion ( m_Player - > IsSprinting ( ) ? 0.8 : 0.2 ) ;
}
2013-07-12 20:01:25 +00:00
}
2012-08-19 19:42:32 +00:00
m_Player - > MoveTo ( Pos ) ;
2012-08-18 09:56:28 +00:00
m_Player - > SetStance ( a_Stance ) ;
m_Player - > SetTouchGround ( a_IsOnGround ) ;
2012-06-14 13:06:06 +00:00
}
2014-01-07 16:47:05 +00:00
void cClientHandle : : HandlePluginMessage ( const AString & a_Channel , const AString & a_Message )
{
2014-02-20 22:24:39 +00:00
if ( a_Channel = = " MC|AdvCdm " )
2014-01-18 17:58:46 +00:00
{
2014-02-20 22:24:39 +00:00
// Command block, set text, Client -> Server
HandleCommandBlockMessage ( a_Message . c_str ( ) , a_Message . size ( ) ) ;
2014-01-19 01:06:19 +00:00
}
2014-02-20 22:24:39 +00:00
else if ( a_Channel = = " MC|Brand " )
2014-01-19 01:06:19 +00:00
{
2014-02-20 22:24:39 +00:00
// Client <-> Server branding exchange
2014-01-19 01:06:19 +00:00
SendPluginMessage ( " MC|Brand " , " MCServer " ) ;
2014-02-20 22:24:39 +00:00
}
2014-04-30 23:25:04 +00:00
else if ( a_Channel = = " MC|ItemName " )
{
HandleAnvilItemName ( a_Message . c_str ( ) , a_Message . size ( ) ) ;
}
2014-02-20 22:24:39 +00:00
else if ( a_Channel = = " REGISTER " )
{
2014-03-06 20:06:53 +00:00
if ( HasPluginChannel ( a_Channel ) )
{
SendPluginMessage ( " UNREGISTER " , a_Channel ) ;
2014-07-17 17:13:23 +00:00
return ; // Can't register again if already taken - kinda defeats the point of plugin messaging!
2014-03-06 20:06:53 +00:00
}
2014-02-20 22:24:39 +00:00
RegisterPluginChannels ( BreakApartPluginChannels ( a_Message ) ) ;
}
else if ( a_Channel = = " UNREGISTER " )
{
UnregisterPluginChannels ( BreakApartPluginChannels ( a_Message ) ) ;
2014-01-18 17:58:46 +00:00
}
2014-03-06 20:06:53 +00: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 17:58:46 +00:00
2014-01-07 16:47:05 +00:00
cPluginManager : : Get ( ) - > CallHookPluginMessage ( * this , a_Channel , a_Message ) ;
}
2014-02-20 22:24:39 +00: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 21:03:23 +00:00
void cClientHandle : : HandleCommandBlockMessage ( const char * a_Data , size_t a_Length )
2014-01-18 17:58:46 +00:00
{
if ( a_Length < 14 )
{
2014-02-07 18:58:52 +00:00
SendChat ( " Failure setting command block command; bad request " , mtFailure ) ;
2014-01-18 17:58:46 +00:00
LOGD ( " Malformed MC|AdvCdm packet. " ) ;
return ;
}
2014-01-18 19:27:54 +00:00
cByteBuffer Buffer ( a_Length ) ;
Buffer . Write ( a_Data , a_Length ) ;
2014-01-18 17:58:46 +00:00
int BlockX , BlockY , BlockZ ;
AString Command ;
2014-01-18 19:27:54 +00:00
char Mode ;
Buffer . ReadChar ( Mode ) ;
switch ( Mode )
2014-01-18 17:58:46 +00:00
{
case 0x00 :
{
2014-01-18 19:27:54 +00:00
Buffer . ReadBEInt ( BlockX ) ;
Buffer . ReadBEInt ( BlockY ) ;
Buffer . ReadBEInt ( BlockZ ) ;
2014-01-18 17:58:46 +00:00
2014-01-18 19:27:54 +00:00
Buffer . ReadVarUTF8String ( Command ) ;
2014-01-18 17:58:46 +00:00
break ;
}
default :
{
2014-02-07 18:58:52 +00:00
SendChat ( " Failure setting command block command; unhandled mode " , mtFailure ) ;
2014-01-18 17:58:46 +00:00
LOGD ( " Unhandled MC|AdvCdm packet mode. " ) ;
return ;
}
}
cWorld * World = m_Player - > GetWorld ( ) ;
2014-01-23 12:57:04 +00:00
if ( World - > AreCommandBlocksEnabled ( ) )
{
World - > SetCommandBlockCommand ( BlockX , BlockY , BlockZ , Command ) ;
2014-02-05 23:24:16 +00:00
2014-02-07 18:58:52 +00:00
SendChat ( " Successfully set command block command " , mtSuccess ) ;
2014-01-23 12:57:04 +00:00
}
else
{
2014-02-07 18:58:52 +00:00
SendChat ( " Command blocks are not enabled on this server " , mtFailure ) ;
2014-01-23 12:57:04 +00:00
}
2014-01-18 17:58:46 +00:00
}
2014-05-05 13:42:41 +00:00
void cClientHandle : : HandleAnvilItemName ( const char * a_Data , size_t a_Length )
2014-04-30 23:25:04 +00:00
{
if ( a_Length < 1 )
{
return ;
}
if ( ( m_Player - > GetWindow ( ) = = NULL ) | | ( m_Player - > GetWindow ( ) - > GetWindowType ( ) ! = cWindow : : wtAnvil ) )
{
return ;
}
2014-05-06 15:31:02 +00:00
AString Name ( a_Data , a_Length ) ;
2014-04-30 23:25:04 +00:00
if ( Name . length ( ) < = 30 )
{
2014-05-06 15:31:02 +00:00
( ( cAnvilWindow * ) m_Player - > GetWindow ( ) ) - > SetRepairedItemName ( Name , m_Player ) ;
2014-04-30 23:25:04 +00:00
}
}
2014-02-04 18:59:05 +00:00
void cClientHandle : : HandleLeftClick ( int a_BlockX , int a_BlockY , int a_BlockZ , eBlockFace a_BlockFace , char a_Status )
2012-06-14 13:06:06 +00:00
{
2013-01-12 04:46:01 +00:00
LOGD ( " HandleLeftClick: {%i, %i, %i}; Face: %i; Stat: %i " ,
a_BlockX , a_BlockY , a_BlockZ , a_BlockFace , a_Status
) ;
2014-03-01 21:24:36 +00:00
m_NumBlockChangeInteractionsThisTick + + ;
if ( ! CheckBlockInteractionsRate ( ) )
{
Kick ( " Too many blocks were destroyed per unit time - hacked client? " ) ;
return ;
}
2014-05-15 17:58:48 +00:00
if (
2014-07-17 17:13:23 +00: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
2014-05-18 21:41:42 +00:00
( ( Diff ( m_Player - > GetPosX ( ) , ( double ) a_BlockX ) > 6 ) | |
2014-05-15 17:58:48 +00:00
( Diff ( m_Player - > GetPosY ( ) , ( double ) a_BlockY ) > 6 ) | |
2014-05-18 21:41:42 +00:00
( Diff ( m_Player - > GetPosZ ( ) , ( double ) a_BlockZ ) > 6 ) )
2014-05-15 17:58:48 +00:00
)
{
m_Player - > GetWorld ( ) - > SendBlockTo ( a_BlockX , a_BlockY , a_BlockZ , m_Player ) ;
return ;
}
2013-01-12 04:46:01 +00: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 13:06:06 +00:00
2013-01-12 04:46:01 +00: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 07:27:39 +00:00
m_Player - > TossEquippedItem ( ) ;
2013-01-12 04:46:01 +00:00
return ;
}
case DIG_STATUS_SHOOT_EAT :
{
cItemHandler * ItemHandler = cItemHandler : : GetItemHandler ( m_Player - > GetEquippedItem ( ) ) ;
2014-06-09 00:06:15 +00:00
if ( ItemHandler - > IsFood ( ) | | ItemHandler - > IsDrinkable ( m_Player - > GetEquippedItem ( ) . m_ItemDamage ) )
2013-01-12 04:46:01 +00:00
{
2013-07-28 17:15:03 +00:00
m_Player - > AbortEating ( ) ;
return ;
2013-01-12 04:46:01 +00: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 12:24:03 +00:00
ItemHandler - > OnItemShoot ( m_Player , a_BlockX , a_BlockY , a_BlockZ , a_BlockFace ) ;
2013-01-12 04:46:01 +00: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 12:59:48 +00:00
case DIG_STATUS_CANCELLED :
{
// Block breaking cancelled by player
2014-05-11 09:56:15 +00:00
FinishDigAnimation ( ) ;
2013-01-13 12:59:48 +00:00
return ;
}
2014-01-15 22:36:19 +00: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-07-17 17:13:23 +00:00
m_Player - > TossEquippedItem ( 64 ) ; // Toss entire slot - if there aren't enough items, the maximum will be ejected
2014-01-15 22:36:19 +00:00
return ;
}
2013-01-12 04:46:01 +00:00
default :
{
ASSERT ( ! " Unhandled DIG_STATUS " ) ;
return ;
}
} // switch (a_Status)
}
2012-06-14 13:06:06 +00:00
2013-01-12 04:46:01 +00:00
2014-02-04 18:59:05 +00:00
void cClientHandle : : HandleBlockDigStarted ( int a_BlockX , int a_BlockY , int a_BlockZ , eBlockFace a_BlockFace , BLOCKTYPE a_OldBlock , NIBBLETYPE a_OldMeta )
2013-01-12 04:46:01 +00:00
{
if (
m_HasStartedDigging & &
( a_BlockX = = m_LastDigBlockX ) & &
( a_BlockY = = m_LastDigBlockY ) & &
( a_BlockZ = = m_LastDigBlockZ )
)
2012-06-14 13:06:06 +00:00
{
2013-01-12 04:46:01 +00:00
// It is a duplicate packet, drop it right away
2012-06-14 13:06:06 +00:00
return ;
}
2014-05-09 21:10:02 +00:00
2014-02-23 19:02:44 +00:00
if (
m_Player - > IsGameModeCreative ( ) & &
2014-02-23 19:31:58 +00:00
ItemCategory : : IsSword ( m_Player - > GetInventory ( ) . GetEquippedItem ( ) . m_ItemType )
2014-02-23 19:02:44 +00:00
)
{
// Players can't destroy blocks with a Sword in the hand.
return ;
}
2014-05-09 21:10:02 +00:00
2014-05-15 17:58:48 +00: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 20:36:34 +00:00
{
2013-01-12 04:46:01 +00:00
m_Player - > GetWorld ( ) - > SendBlockTo ( a_BlockX , a_BlockY , a_BlockZ , m_Player ) ;
2012-08-18 09:56:28 +00:00
return ;
2012-07-15 20:36:34 +00:00
}
2014-05-15 17:58:48 +00:00
2013-01-12 04:46:01 +00: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 13:06:06 +00:00
2014-06-17 11:25:36 +00:00
// Check for clickthrough-blocks:
2014-06-17 11:44:07 +00:00
/* When the user breaks a fire block, the client send the wrong block location.
We must find the right block with the face direction . */
2014-05-28 14:59:51 +00:00
if ( a_BlockFace ! = BLOCK_FACE_NONE )
{
int pX = a_BlockX ;
int pY = a_BlockY ;
int pZ = a_BlockZ ;
2014-07-17 17:13:23 +00:00
AddFaceDirection ( pX , pY , pZ , a_BlockFace ) ; // Get the block in front of the clicked coordinates (m_bInverse defaulted to false)
2014-05-28 14:59:51 +00:00
cBlockHandler * Handler = cBlockInfo : : GetHandler ( m_Player - > GetWorld ( ) - > GetBlock ( pX , pY , pZ ) ) ;
if ( Handler - > IsClickedThrough ( ) )
{
cChunkInterface ChunkInterface ( m_Player - > GetWorld ( ) - > GetChunkMap ( ) ) ;
Handler - > OnDigging ( ChunkInterface , * m_Player - > GetWorld ( ) , m_Player , pX , pY , pZ ) ;
return ;
}
}
2013-02-14 21:47:57 +00:00
if (
2013-10-29 20:45:31 +00:00
( m_Player - > IsGameModeCreative ( ) ) | | // In creative mode, digging is done immediately
2014-03-01 19:34:19 +00:00
cBlockInfo : : IsOneHitDig ( a_OldBlock ) // One-hit blocks get destroyed immediately, too
2013-02-14 21:47:57 +00:00
)
2012-06-14 13:06:06 +00:00
{
2013-01-12 04:46:01 +00:00
HandleBlockDigFinished ( a_BlockX , a_BlockY , a_BlockZ , a_BlockFace , a_OldBlock , a_OldMeta ) ;
2012-06-14 13:06:06 +00:00
return ;
}
2012-11-12 11:10:01 +00:00
2013-01-12 04:46:01 +00: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 16:35:48 +00:00
cChunkInterface ChunkInterface ( World - > GetChunkMap ( ) ) ;
2014-03-02 19:25:05 +00:00
cBlockHandler * Handler = cBlockInfo : : GetHandler ( a_OldBlock ) ;
2014-02-01 16:35:48 +00:00
Handler - > OnDigging ( ChunkInterface , * World , m_Player , a_BlockX , a_BlockY , a_BlockZ ) ;
2013-01-12 04:46:01 +00:00
cItemHandler * ItemHandler = cItemHandler : : GetItemHandler ( m_Player - > GetEquippedItem ( ) ) ;
2013-05-19 18:22:37 +00:00
ItemHandler - > OnDiggingBlock ( World , m_Player , m_Player - > GetEquippedItem ( ) , a_BlockX , a_BlockY , a_BlockZ , a_BlockFace ) ;
2013-01-12 04:46:01 +00:00
}
2012-06-14 13:06:06 +00:00
2012-11-12 11:10:01 +00:00
2013-01-12 04:46:01 +00:00
2014-02-04 18:59:05 +00:00
void cClientHandle : : HandleBlockDigFinished ( int a_BlockX , int a_BlockY , int a_BlockZ , eBlockFace a_BlockFace , BLOCKTYPE a_OldBlock , NIBBLETYPE a_OldMeta )
2013-01-12 04:46:01 +00: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 09:54:36 +00:00
{
2013-01-12 04:46:01 +00: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 21:16:08 +00:00
( m_HasStartedDigging ? " True " : " False " )
2013-01-12 04:46:01 +00:00
) ;
return ;
2012-09-25 09:54:36 +00:00
}
2013-01-12 04:46:01 +00:00
2014-05-11 09:56:15 +00:00
FinishDigAnimation ( ) ;
2012-09-25 09:54:36 +00:00
2014-05-15 17:58:48 +00:00
cWorld * World = m_Player - > GetWorld ( ) ;
2013-01-12 04:46:01 +00:00
cItemHandler * ItemHandler = cItemHandler : : GetItemHandler ( m_Player - > GetEquippedItem ( ) ) ;
2014-05-09 21:10:02 +00:00
if ( cRoot : : Get ( ) - > GetPluginManager ( ) - > CallHookPlayerBreakingBlock ( * m_Player , a_BlockX , a_BlockY , a_BlockZ , a_BlockFace , a_OldBlock , a_OldMeta ) )
2012-09-25 09:54:36 +00:00
{
2014-05-09 21:10:02 +00: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 09:54:36 +00:00
}
2013-01-12 04:46:01 +00:00
if ( a_OldBlock = = E_BLOCK_AIR )
2012-06-14 13:06:06 +00:00
{
2013-11-10 22:20:25 +00:00
LOGD ( " Dug air - what the function? " ) ;
2013-01-12 04:46:01 +00:00
return ;
2012-08-18 09:56:28 +00:00
}
2014-05-09 21:43:00 +00:00
2013-05-19 18:22:37 +00:00
ItemHandler - > OnBlockDestroyed ( World , m_Player , m_Player - > GetEquippedItem ( ) , a_BlockX , a_BlockY , a_BlockZ ) ;
2013-09-21 16:08:05 +00:00
// The ItemHandler is also responsible for spawning the pickups
2014-02-01 14:01:13 +00:00
cChunkInterface ChunkInterface ( World - > GetChunkMap ( ) ) ;
BlockHandler ( a_OldBlock ) - > OnDestroyedByPlayer ( ChunkInterface , * World , m_Player , a_BlockX , a_BlockY , a_BlockZ ) ;
2013-11-10 22:20:25 +00:00
World - > BroadcastSoundParticleEffect ( 2001 , a_BlockX , a_BlockY , a_BlockZ , a_OldBlock , this ) ;
2013-01-12 04:46:01 +00:00
World - > DigBlock ( a_BlockX , a_BlockY , a_BlockZ ) ;
2012-10-06 20:53:08 +00:00
2013-01-12 04:46:01 +00:00
cRoot : : Get ( ) - > GetPluginManager ( ) - > CallHookPlayerBrokenBlock ( * m_Player , a_BlockX , a_BlockY , a_BlockZ , a_BlockFace , a_OldBlock , a_OldMeta ) ;
2012-06-14 13:06:06 +00:00
}
2014-05-11 09:56:15 +00:00
void cClientHandle : : FinishDigAnimation ( )
2014-05-09 21:10:02 +00:00
{
2014-07-17 17:13:23 +00:00
if ( ! m_HasStartedDigging ) // Hasn't received the DIG_STARTED packet
2014-05-09 21:10:02 +00:00
{
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 18:59:05 +00: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 13:06:06 +00:00
{
2013-01-12 04:46:01 +00:00
LOGD ( " HandleRightClick: {%d, %d, %d}, face %d, HeldItem: %s " ,
2012-11-18 21:50:05 +00:00
a_BlockX , a_BlockY , a_BlockZ , a_BlockFace , ItemToFullString ( a_HeldItem ) . c_str ( )
2012-09-07 20:53:37 +00:00
) ;
2014-03-05 14:10:20 +00:00
cWorld * World = m_Player - > GetWorld ( ) ;
2014-05-15 17:58:48 +00:00
if (
2014-06-08 09:32:52 +00: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 17:58:48 +00:00
)
{
2014-06-08 09:32:52 +00: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 17:58:48 +00:00
{
World - > SendBlockTo ( a_BlockX , a_BlockY + 1 , a_BlockZ , m_Player ) ; // 2 block high things
}
2014-06-08 09:32:52 +00: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 17:58:48 +00:00
return ;
}
2013-01-12 04:46:01 +00: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 14:10:20 +00: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 14:55:47 +00:00
if ( a_BlockFace ! = BLOCK_FACE_NONE )
2013-01-12 04:46:01 +00:00
{
AddFaceDirection ( a_BlockX , a_BlockY , a_BlockZ , a_BlockFace ) ;
2014-03-05 14:10:20 +00:00
World - > SendBlockTo ( a_BlockX , a_BlockY , a_BlockZ , m_Player ) ;
2014-05-15 17:58:48 +00:00
World - > SendBlockTo ( a_BlockX , a_BlockY + 1 , a_BlockZ , m_Player ) ; // 2 block high things
2014-05-09 21:10:02 +00:00
m_Player - > GetInventory ( ) . SendEquippedSlot ( ) ;
2013-01-12 04:46:01 +00:00
}
return ;
}
2014-03-28 22:52:04 +00:00
m_NumBlockChangeInteractionsThisTick + + ;
2013-01-12 04:46:01 +00:00
2012-06-14 13:06:06 +00:00
if ( ! CheckBlockInteractionsRate ( ) )
{
2014-03-02 16:28:51 +00:00
Kick ( " Too many blocks were placed/interacted with per unit time - hacked client? " ) ;
2012-06-14 13:06:06 +00:00
return ;
}
2013-05-19 18:22:37 +00:00
const cItem & Equipped = m_Player - > GetInventory ( ) . GetEquippedItem ( ) ;
2012-06-14 13:06:06 +00:00
2012-09-07 20:53:37 +00:00
if ( ( Equipped . m_ItemType ! = a_HeldItem . m_ItemType ) & & ( a_HeldItem . m_ItemType ! = - 1 ) )
2012-06-14 13:06:06 +00:00
{
2012-09-07 20:53:37 +00:00
// Only compare ItemType, not meta (torches have different metas)
2014-07-17 14:33:09 +00:00
// The -1 check is there because sometimes the client sends -1 instead of the held item
2012-09-07 20:53:37 +00:00
// ( http://forum.mc-server.org/showthread.php?tid=549&pid=4502#pid4502 )
2012-06-14 13:06:06 +00:00
LOGWARN ( " Player %s tried to place a block that was not equipped (exp %d, got %d) " ,
2012-08-18 09:56:28 +00:00
m_Username . c_str ( ) , Equipped . m_ItemType , a_HeldItem . m_ItemType
2012-06-14 13:06:06 +00:00
) ;
2012-08-18 09:56:28 +00: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 13:18:26 +00:00
if ( a_BlockFace ! = BLOCK_FACE_NONE )
2012-08-18 09:56:28 +00:00
{
2013-01-12 04:46:01 +00:00
AddFaceDirection ( a_BlockX , a_BlockY , a_BlockZ , a_BlockFace ) ;
2014-03-05 14:10:20 +00:00
World - > SendBlockTo ( a_BlockX , a_BlockY , a_BlockZ , m_Player ) ;
2012-08-18 09:56:28 +00:00
}
2012-06-14 13:06:06 +00:00
return ;
}
2013-01-12 04:46:01 +00:00
BLOCKTYPE BlockType ;
NIBBLETYPE BlockMeta ;
World - > GetBlockTypeMeta ( a_BlockX , a_BlockY , a_BlockZ , BlockType , BlockMeta ) ;
2014-03-02 19:25:05 +00:00
cBlockHandler * BlockHandler = cBlockInfo : : GetHandler ( BlockType ) ;
2012-12-19 21:19:36 +00:00
2013-08-30 12:24:03 +00:00
if ( BlockHandler - > IsUseable ( ) & & ! m_Player - > IsCrouched ( ) )
2012-06-14 13:06:06 +00:00
{
2013-01-12 04:46:01 +00: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 13:06:32 +00: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-12 04:46:01 +00: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 20:10:16 +00:00
}
2013-01-12 04:46:01 +00:00
2014-06-09 00:06:15 +00:00
short EquippedDamage = Equipped . m_ItemDamage ;
2013-01-12 04:46:01 +00:00
cItemHandler * ItemHandler = cItemHandler : : GetItemHandler ( Equipped . m_ItemType ) ;
2014-03-29 13:18:26 +00:00
if ( ItemHandler - > IsPlaceable ( ) & & ( a_BlockFace ! = BLOCK_FACE_NONE ) )
2012-08-06 20:10:16 +00:00
{
2013-01-12 04:46:01 +00:00
HandlePlaceBlock ( a_BlockX , a_BlockY , a_BlockZ , a_BlockFace , a_CursorX , a_CursorY , a_CursorZ , * ItemHandler ) ;
}
2014-07-18 08:56:26 +00:00
else if ( ( ItemHandler - > IsFood ( ) | | ItemHandler - > IsDrinkable ( EquippedDamage ) ) )
2013-01-12 04:46:01 +00:00
{
2014-07-18 08:56:26 +00:00
if ( ( m_Player - > IsSatiated ( ) | | m_Player - > IsGameModeCreative ( ) ) & &
ItemHandler - > IsFood ( ) )
2013-07-28 20:55:09 +00:00
{
2014-07-18 08:56:26 +00:00
// The player is satiated or in creative, and trying to eat
2013-07-28 20:55:09 +00:00
return ;
}
2013-07-28 17:15:03 +00:00
m_Player - > StartEating ( ) ;
if ( PlgMgr - > CallHookPlayerEating ( * m_Player ) )
2012-06-14 13:06:06 +00:00
{
2013-07-28 17:15:03 +00:00
// A plugin won't let us eat, abort (send the proper packets to the client, too):
m_Player - > AbortEating ( ) ;
2012-08-18 09:56:28 +00:00
}
2013-01-12 04:46:01 +00:00
}
else
{
if ( PlgMgr - > CallHookPlayerUsingItem ( * m_Player , a_BlockX , a_BlockY , a_BlockZ , a_BlockFace , a_CursorX , a_CursorY , a_CursorZ ) )
2012-06-14 13:06:06 +00:00
{
2013-01-12 04:46:01 +00:00
// A plugin doesn't agree with using the item, abort
return ;
}
2013-05-19 18:22:37 +00:00
ItemHandler - > OnItemUse ( World , m_Player , Equipped , a_BlockX , a_BlockY , a_BlockZ , a_BlockFace ) ;
2013-01-12 04:46:01 +00:00
PlgMgr - > CallHookPlayerUsedItem ( * m_Player , a_BlockX , a_BlockY , a_BlockZ , a_BlockFace , a_CursorX , a_CursorY , a_CursorZ ) ;
}
}
2012-06-14 13:06:06 +00:00
2012-07-15 20:36:34 +00:00
2012-06-14 13:06:06 +00:00
2014-02-04 18:59:05 +00: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-12 04:46:01 +00:00
{
2014-03-23 22:32:45 +00:00
BLOCKTYPE EquippedBlock = ( BLOCKTYPE ) ( m_Player - > GetEquippedItem ( ) . m_ItemType ) ;
2013-01-12 04:46:01 +00:00
if ( a_BlockFace < 0 )
{
// Clicked in air
return ;
}
cWorld * World = m_Player - > GetWorld ( ) ;
2013-08-23 18:38:39 +00:00
BLOCKTYPE ClickedBlock ;
NIBBLETYPE ClickedBlockMeta ;
NIBBLETYPE EquippedBlockDamage = ( NIBBLETYPE ) ( m_Player - > GetEquippedItem ( ) . m_ItemDamage ) ;
2013-08-25 14:11:19 +00: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-12 04:46:01 +00:00
2013-08-23 18:38:39 +00:00
World - > GetBlockTypeMeta ( a_BlockX , a_BlockY , a_BlockZ , ClickedBlock , ClickedBlockMeta ) ;
2013-08-25 14:11:19 +00:00
// Special slab handling - placing a slab onto another slab produces a dblslab instead:
2013-08-23 18:38:39 +00:00
if (
2013-08-25 14:11:19 +00:00
cBlockSlabHandler : : IsAnySlabType ( ClickedBlock ) & & // Is there a slab already?
cBlockSlabHandler : : IsAnySlabType ( EquippedBlock ) & & // Is the player placing another slab?
2014-02-28 21:32:10 +00:00
( ( ClickedBlockMeta & 0x07 ) = = EquippedBlockDamage ) & & // Is it the same slab type?
2013-08-25 14:11:19 +00:00
(
2014-03-28 22:52:04 +00: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 18:38:39 +00:00
)
2013-08-25 14:11:19 +00:00
)
2013-01-12 04:46:01 +00:00
{
2013-12-06 19:22:25 +00: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 19:34:11 +00:00
if ( ( ClickedBlockMeta & 0x08 ) & & ( a_BlockFace = = BLOCK_FACE_TOP ) )
2013-12-02 16:32:28 +00: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-12 04:46:01 +00:00
}
else
{
2013-08-24 17:46:19 +00:00
// Check if the block ignores build collision (water, grass etc.):
2013-11-30 00:31:21 +00:00
if (
BlockHandler ( ClickedBlock ) - > DoesIgnoreBuildCollision ( ) | |
BlockHandler ( ClickedBlock ) - > DoesIgnoreBuildCollision ( m_Player , ClickedBlockMeta )
)
2013-01-12 04:46:01 +00:00
{
2014-02-01 14:01:13 +00:00
cChunkInterface ChunkInterface ( World - > GetChunkMap ( ) ) ;
BlockHandler ( ClickedBlock ) - > OnDestroyedByPlayer ( ChunkInterface , * World , m_Player , a_BlockX , a_BlockY , a_BlockZ ) ;
2013-01-12 04:46:01 +00:00
}
2013-11-30 00:31:21 +00:00
else
2012-08-18 09:56:28 +00:00
{
2013-08-23 18:38:39 +00:00
AddFaceDirection ( a_BlockX , a_BlockY , a_BlockZ , a_BlockFace ) ;
2013-08-24 17:46:19 +00:00
2013-08-25 14:11:19 +00: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-30 00:31:21 +00:00
NIBBLETYPE PlaceMeta ;
BLOCKTYPE PlaceBlock ;
World - > GetBlockTypeMeta ( a_BlockX , a_BlockY , a_BlockZ , PlaceBlock , PlaceMeta ) ;
2013-08-25 14:11:19 +00:00
2013-08-24 17:46:19 +00: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 14:11:19 +00:00
if ( cBlockSlabHandler : : IsAnySlabType ( PlaceBlock ) )
2013-08-23 18:38:39 +00:00
{
2013-08-25 14:11:19 +00:00
// It's a slab, don't do checks and proceed to double-slabbing
2013-08-23 18:38:39 +00:00
}
else
{
2013-11-30 00:31:21 +00:00
if (
! BlockHandler ( PlaceBlock ) - > DoesIgnoreBuildCollision ( ) & &
! BlockHandler ( PlaceBlock ) - > DoesIgnoreBuildCollision ( m_Player , PlaceMeta )
)
2013-08-23 18:38:39 +00:00
{
// Tried to place a block *into* another?
2013-11-30 00:31:21 +00:00
// Happens when you place a block aiming at side of block with a torch on it or stem beside it
2013-08-23 18:38:39 +00:00
return ;
}
}
2012-06-14 13:06:06 +00:00
}
}
2013-08-24 17:46:19 +00:00
2013-01-12 04:46:01 +00: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 10:29:08 +00:00
World - > SendBlockTo ( a_BlockX , a_BlockY , a_BlockZ , m_Player ) ;
2014-05-09 21:10:02 +00:00
m_Player - > GetInventory ( ) . SendEquippedSlot ( ) ;
2013-01-12 04:46:01 +00: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 21:10:02 +00:00
m_Player - > GetInventory ( ) . SendEquippedSlot ( ) ;
2013-01-12 04:46:01 +00:00
return ;
}
// The actual block placement:
World - > SetBlock ( a_BlockX , a_BlockY , a_BlockZ , BlockType , BlockMeta ) ;
2014-03-29 10:25:40 +00:00
if ( ! m_Player - > IsGameModeCreative ( ) )
2013-01-12 04:46:01 +00:00
{
2013-05-24 07:30:39 +00:00
m_Player - > GetInventory ( ) . RemoveOneEquippedItem ( ) ;
2013-01-12 04:46:01 +00:00
}
2014-01-31 23:17:41 +00:00
cChunkInterface ChunkInterface ( World - > GetChunkMap ( ) ) ;
2014-02-01 13:06:32 +00: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-12 04:46:01 +00:00
// Step sound with 0.8f pitch is used as block placement sound
2014-07-13 00:08:02 +00:00
World - > BroadcastSoundEffect ( NewBlock - > GetStepSound ( ) , ( double ) a_BlockX , ( double ) a_BlockY , ( double ) a_BlockZ , 1.0f , 0.8f ) ;
2013-01-12 04:46:01 +00: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 13:06:06 +00:00
}
2012-08-18 10:38:15 +00:00
void cClientHandle : : HandleChat ( const AString & a_Message )
2012-06-14 13:06:06 +00:00
{
2013-08-14 18:08:05 +00:00
// We no longer need to postpone message processing, because the messages already arrive in the Tick thread
2013-07-03 07:47:35 +00:00
2013-08-14 18:08:05 +00:00
// If a command, perform it:
AString Message ( a_Message ) ;
if ( cRoot : : Get ( ) - > GetServer ( ) - > Command ( * this , Message ) )
{
return ;
}
2014-02-16 22:51:32 +00: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-07-17 14:33:09 +00:00
{
2014-04-18 19:09:44 +00:00
Color . clear ( ) ;
2014-02-16 22:51:32 +00:00
}
Msg . AddTextPart ( AString ( " < " ) + m_Player - > GetName ( ) + " > " , Color ) ;
Msg . ParseText ( a_Message ) ;
Msg . UnderlineUrls ( ) ;
2013-08-14 18:08:05 +00:00
m_Player - > GetWorld ( ) - > BroadcastChat ( Msg ) ;
2012-06-14 13:06:06 +00:00
}
2012-08-19 11:51:17 +00:00
void cClientHandle : : HandlePlayerLook ( float a_Rotation , float a_Pitch , bool a_IsOnGround )
2012-06-14 13:06:06 +00:00
{
2013-09-08 16:36:06 +00:00
if ( ( m_Player = = NULL ) | | ( m_State ! = csPlaying ) )
{
return ;
}
2014-01-16 19:00:49 +00:00
m_Player - > SetYaw ( a_Rotation ) ;
2013-04-02 06:48:31 +00:00
m_Player - > SetHeadYaw ( a_Rotation ) ;
2012-08-19 11:51:17 +00:00
m_Player - > SetPitch ( a_Pitch ) ;
m_Player - > SetTouchGround ( a_IsOnGround ) ;
2012-06-14 13:06:06 +00:00
}
2014-05-26 14:38:14 +00: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 13:06:06 +00:00
{
2014-05-26 21:13:40 +00:00
HandlePlayerLook ( a_Rotation , a_Pitch , a_IsOnGround ) ;
2014-05-27 07:28:46 +00:00
HandlePlayerPos ( a_PosX , a_PosY , a_PosZ , a_Stance , a_IsOnGround ) ;
2012-06-14 13:06:06 +00:00
}
2012-08-19 11:51:17 +00:00
void cClientHandle : : HandleAnimation ( char a_Animation )
2012-06-14 13:06:06 +00:00
{
2013-12-18 16:11:15 +00:00
if ( cPluginManager : : Get ( ) - > CallHookPlayerAnimation ( * m_Player , a_Animation ) )
2013-08-11 10:12:20 +00:00
{
// Plugin disagrees, bail out
return ;
}
2013-12-19 16:03:43 +00:00
// Because the animation ID sent to servers by clients are different to those sent back, we need this
switch ( a_Animation )
{
2014-07-17 17:13:23 +00:00
case 0 : // No animation - wiki.vg doesn't say that client has something specific for it, so I suppose it will just become -1
2013-12-19 16:03:43 +00:00
case 1 :
case 2 :
case 3 :
{
2014-07-17 17:13:23 +00:00
a_Animation - - ; // Offset by -1
2013-12-19 16:03:43 +00:00
break ;
}
2014-07-17 14:33:09 +00:00
case 5 :
2013-12-19 16:03:43 +00:00
case 6 :
case 7 :
{
2014-07-17 17:13:23 +00:00
a_Animation - = 2 ; // Offset by -2
2013-12-19 16:03:43 +00:00
break ;
}
2014-07-17 17:13:23 +00:00
default : // Anything else is the same
{
2013-12-19 16:03:43 +00:00
break ;
2014-07-17 17:13:23 +00:00
}
2013-12-19 16:03:43 +00:00
}
2013-12-06 23:47:07 +00:00
m_Player - > GetWorld ( ) - > BroadcastEntityAnimation ( * m_Player , a_Animation , this ) ;
2012-06-14 13:06:06 +00:00
}
2012-08-19 11:51:17 +00:00
void cClientHandle : : HandleSlotSelected ( short a_SlotNum )
2012-06-14 13:06:06 +00:00
{
2013-05-19 18:22:37 +00:00
m_Player - > GetInventory ( ) . SetEquippedSlotNum ( a_SlotNum ) ;
2012-08-19 11:51:17 +00:00
m_Player - > GetWorld ( ) - > BroadcastEntityEquipment ( * m_Player , 0 , m_Player - > GetInventory ( ) . GetEquippedItem ( ) , this ) ;
2012-06-14 13:06:06 +00:00
}
2013-09-05 22:04:49 +00:00
void cClientHandle : : HandleSteerVehicle ( float a_Forward , float a_Sideways )
{
m_Player - > SteerVehicle ( a_Forward , a_Sideways ) ;
}
2012-08-19 11:51:17 +00:00
void cClientHandle : : HandleWindowClose ( char a_WindowID )
2012-06-14 13:06:06 +00:00
{
2013-05-30 19:34:09 +00:00
m_Player - > CloseWindowIfID ( a_WindowID ) ;
2012-06-14 13:06:06 +00:00
}
2013-05-08 09:45:07 +00:00
void cClientHandle : : HandleWindowClick ( char a_WindowID , short a_SlotNum , eClickAction a_ClickAction , const cItem & a_HeldItem )
2012-06-14 13:06:06 +00:00
{
2013-05-08 09:45:07 +00:00
LOGD ( " WindowClick: WinID %d, SlotNum %d, action: %s, Item %s x %d " ,
a_WindowID , a_SlotNum , ClickActionToString ( a_ClickAction ) ,
2012-09-20 13:25:54 +00:00
ItemToString ( a_HeldItem ) . c_str ( ) , a_HeldItem . m_ItemCount
) ;
2012-06-14 13:06:06 +00: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 09:45:07 +00:00
Window - > Clicked ( * m_Player , a_WindowID , a_SlotNum , a_ClickAction , a_HeldItem ) ;
2012-06-14 13:06:06 +00:00
}
2012-08-19 19:42:32 +00:00
void cClientHandle : : HandleUpdateSign (
2014-07-17 14:33:09 +00:00
int a_BlockX , int a_BlockY , int a_BlockZ ,
const AString & a_Line1 , const AString & a_Line2 ,
2012-08-19 19:42:32 +00:00
const AString & a_Line3 , const AString & a_Line4
)
2012-06-14 13:06:06 +00:00
{
cWorld * World = m_Player - > GetWorld ( ) ;
2012-09-01 21:44:18 +00:00
World - > UpdateSign ( a_BlockX , a_BlockY , a_BlockZ , a_Line1 , a_Line2 , a_Line3 , a_Line4 , m_Player ) ;
2012-06-14 13:06:06 +00:00
}
2012-08-19 19:42:32 +00:00
void cClientHandle : : HandleUseEntity ( int a_TargetEntityID , bool a_IsLeftClick )
2012-06-14 13:06:06 +00:00
{
2013-03-03 19:05:11 +00:00
// TODO: Let plugins interfere via a hook
2013-07-12 20:01:25 +00:00
// If it is a right click, call the entity's OnRightClicked() handler:
2012-08-19 19:42:32 +00:00
if ( ! a_IsLeftClick )
2012-06-14 13:06:06 +00:00
{
2013-03-03 19:05:11 +00:00
class cRclkEntity : public cEntityCallback
{
cPlayer & m_Player ;
virtual bool Item ( cEntity * a_Entity ) override
{
2013-08-02 06:44:06 +00:00
if ( cPluginManager : : Get ( ) - > CallHookPlayerRightClickingEntity ( m_Player , * a_Entity ) )
{
return false ;
}
2013-03-03 19:05:11 +00:00
a_Entity - > OnRightClicked ( m_Player ) ;
2013-07-12 20:01:25 +00:00
return false ;
2013-03-03 19:05:11 +00: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 13:06:06 +00:00
return ;
}
2013-07-12 20:01:25 +00:00
// If it is a left click, attack the entity:
2012-06-14 13:06:06 +00:00
class cDamageEntity : public cEntityCallback
{
virtual bool Item ( cEntity * a_Entity ) override
{
2012-11-16 16:03:56 +00:00
if ( ! a_Entity - > GetWorld ( ) - > IsPVPEnabled ( ) )
2012-10-10 19:46:12 +00:00
{
2013-07-12 20:01:25 +00:00
// PVP is disabled, disallow players hurting other players:
if ( a_Entity - > IsPlayer ( ) )
2012-10-10 19:46:12 +00:00
{
// Player is hurting another player which is not allowed when PVP is disabled so ignore it
return true ;
}
}
2013-07-12 20:01:25 +00:00
a_Entity - > TakeDamage ( * m_Attacker ) ;
return false ;
2012-06-14 13:06:06 +00:00
}
public :
2012-12-21 11:04:08 +00:00
cPawn * m_Attacker ;
2012-06-14 13:06:06 +00:00
} Callback ;
2012-12-21 11:04:08 +00:00
Callback . m_Attacker = m_Player ;
2012-06-14 13:06:06 +00:00
cWorld * World = m_Player - > GetWorld ( ) ;
2013-07-12 20:01:25 +00:00
if ( World - > DoWithEntityByID ( a_TargetEntityID , Callback ) )
{
// Any kind of an attack implies food exhaustion
m_Player - > AddFoodExhaustion ( 0.3 ) ;
}
2012-06-14 13:06:06 +00:00
}
void cClientHandle : : HandleRespawn ( void )
{
2013-09-08 16:36:06 +00:00
if ( m_Player = = NULL )
2012-10-17 12:19:20 +00:00
{
Destroy ( ) ;
return ;
}
2012-06-14 13:06:06 +00:00
m_Player - > Respawn ( ) ;
2013-01-12 04:46:01 +00:00
cRoot : : Get ( ) - > GetPluginManager ( ) - > CallHookPlayerSpawned ( * m_Player ) ;
2012-06-14 13:06:06 +00:00
}
2012-08-19 19:42:32 +00:00
void cClientHandle : : HandleDisconnect ( const AString & a_Reason )
2012-06-14 13:06:06 +00:00
{
2013-12-18 18:17:17 +00:00
LOGD ( " Received d/c packet from %s with reason \" %s \" " , m_Username . c_str ( ) , a_Reason . c_str ( ) ) ;
2014-02-03 21:12:44 +00:00
2014-05-02 09:18:02 +00:00
cRoot : : Get ( ) - > GetPluginManager ( ) - > CallHookDisconnect ( * this , a_Reason ) ;
2014-02-03 21:12:44 +00:00
2013-04-13 21:02:10 +00:00
m_HasSentDC = true ;
2012-06-14 13:06:06 +00:00
Destroy ( ) ;
}
2012-08-19 19:42:32 +00:00
void cClientHandle : : HandleKeepAlive ( int a_KeepAliveID )
2012-06-14 13:06:06 +00:00
{
2012-08-19 19:42:32 +00:00
if ( a_KeepAliveID = = m_PingID )
2012-06-14 13:06:06 +00:00
{
cTimer t1 ;
m_Ping = ( short ) ( ( t1 . GetNowTime ( ) - m_PingStartTime ) / 2 ) ;
}
}
2012-08-19 19:42:32 +00:00
2012-09-05 20:30:27 +00:00
bool cClientHandle : : HandleHandshake ( const AString & a_Username )
{
if ( ! cRoot : : Get ( ) - > GetPluginManager ( ) - > CallHookHandshake ( this , a_Username ) )
{
2013-08-11 17:18:06 +00:00
if ( cRoot : : Get ( ) - > GetServer ( ) - > GetNumPlayers ( ) > = cRoot : : Get ( ) - > GetServer ( ) - > GetMaxPlayers ( ) )
2012-09-05 20:30:27 +00:00
{
Kick ( " The server is currently full :(-- Try again later " ) ;
return false ;
}
}
return true ;
}
2014-03-08 16:55:53 +00:00
void cClientHandle : : HandleEntityCrouch ( int a_EntityID , bool a_IsCrouching )
2012-09-29 20:43:42 +00:00
{
2013-08-11 17:18:06 +00:00
if ( a_EntityID ! = m_Player - > GetUniqueID ( ) )
2012-09-29 20:43:42 +00:00
{
// We should only receive entity actions from the entity that is performing the action
return ;
}
2014-03-08 16:55:53 +00:00
m_Player - > SetCrouch ( a_IsCrouching ) ;
}
void cClientHandle : : HandleEntityLeaveBed ( int a_EntityID )
{
if ( a_EntityID ! = m_Player - > GetUniqueID ( ) )
2012-09-29 20:43:42 +00:00
{
2014-03-08 16:55:53 +00:00
// We should only receive entity actions from the entity that is performing the action
return ;
2012-09-29 20:43:42 +00:00
}
2014-03-08 16:55:53 +00: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 20:43:42 +00:00
}
2013-07-05 21:11:06 +00:00
void cClientHandle : : HandleUnmount ( void )
{
if ( m_Player = = NULL )
{
return ;
}
m_Player - > Detach ( ) ;
}
2013-07-30 20:48:59 +00:00
void cClientHandle : : HandleTabCompletion ( const AString & a_Text )
{
AStringVector Results ;
m_Player - > GetWorld ( ) - > TabCompleteUserName ( a_Text , Results ) ;
2013-07-31 09:16:11 +00:00
cRoot : : Get ( ) - > GetPluginManager ( ) - > TabCompleteCommand ( a_Text , Results , m_Player ) ;
2013-07-30 20:48:59 +00:00
if ( Results . empty ( ) )
{
return ;
}
std : : sort ( Results . begin ( ) , Results . end ( ) ) ;
SendTabCompletionResults ( Results ) ;
}
2014-04-04 08:13:25 +00:00
void cClientHandle : : SendData ( const char * a_Data , size_t a_Size )
2012-08-27 17:31:16 +00:00
{
2013-08-22 06:17:26 +00: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 17:31:16 +00:00
{
cCSLock Lock ( m_CSOutgoingData ) ;
2012-09-06 08:33:43 +00: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 08:13:25 +00:00
size_t CanFit = m_OutgoingData . GetFreeSpace ( ) ;
2012-09-06 08:33:43 +00: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 17:31:16 +00:00
{
2012-09-06 08:33:43 +00: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 20:58:58 +00:00
size_t CanFit = m_OutgoingData . GetFreeSpace ( ) ;
2012-09-06 08:33:43 +00: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 17:31:16 +00:00
}
2012-09-06 08:33:43 +00:00
} // Lock(m_CSOutgoingData)
2012-08-27 17:31:16 +00:00
// Notify SocketThreads that we have something to write:
cRoot : : Get ( ) - > GetServer ( ) - > NotifyClientWrite ( this ) ;
}
2014-06-08 19:58:08 +00:00
void cClientHandle : : RemoveFromWorld ( void )
2013-08-14 08:24:34 +00: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-06-12 14:21:07 +00:00
// Here, we set last streamed values to bogus ones so everything is resent
2013-08-14 08:24:34 +00:00
m_LastStreamedChunkX = 0x7fffffff ;
m_LastStreamedChunkZ = 0x7fffffff ;
2013-09-08 16:36:06 +00:00
m_HasSentPlayerChunk = false ;
2013-08-14 08:24:34 +00:00
}
2012-06-14 13:06:06 +00:00
bool cClientHandle : : CheckBlockInteractionsRate ( void )
{
ASSERT ( m_Player ! = NULL ) ;
ASSERT ( m_Player - > GetWorld ( ) ! = NULL ) ;
2014-03-01 21:24:36 +00:00
if ( m_NumBlockChangeInteractionsThisTick > MAX_BLOCK_CHANGE_INTERACTIONS )
2012-06-14 13:06:06 +00:00
{
2014-03-01 21:24:36 +00:00
return false ;
2012-06-14 13:06:06 +00:00
}
2014-03-01 21:24:36 +00:00
2012-06-14 13:06:06 +00:00
return true ;
}
void cClientHandle : : Tick ( float a_Dt )
{
2013-08-13 20:45:29 +00: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 21:38:20 +00:00
m_TimeSinceLastPacket + = a_Dt ;
if ( m_TimeSinceLastPacket > 30000.f ) // 30 seconds time-out
2012-06-14 13:06:06 +00:00
{
2012-08-25 21:59:13 +00:00
SendDisconnect ( " Nooooo!! You timed out! D: Come back! " ) ;
2012-06-14 13:06:06 +00:00
Destroy ( ) ;
}
2013-08-12 06:35:13 +00:00
if ( m_Player = = NULL )
{
return ;
}
2013-09-08 16:36:06 +00:00
// If the chunk the player's in was just sent, spawn the player:
2013-12-15 19:12:40 +00:00
if ( m_HasSentPlayerChunk & & ( m_State = = csDownloadingWorld ) )
2013-09-08 16:36:06 +00:00
{
m_Protocol - > SendPlayerMoveLook ( ) ;
m_State = csPlaying ;
}
2013-07-03 07:47:35 +00:00
// Send a ping packet:
2014-04-15 21:40:06 +00:00
if ( m_State = = csPlaying )
2012-06-14 13:06:06 +00:00
{
2014-04-15 21:40:06 +00: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 13:06:06 +00:00
}
2012-09-25 09:54:36 +00:00
// Handle block break animation:
2013-08-12 06:35:13 +00:00
if ( m_BlockDigAnimStage > - 1 )
2012-09-25 09:54:36 +00:00
{
2013-01-12 04:46:01 +00:00
int lastAnimVal = m_BlockDigAnimStage ;
m_BlockDigAnimStage + = ( int ) ( m_BlockDigAnimSpeed * a_Dt ) ;
if ( m_BlockDigAnimStage > 9000 )
2012-09-25 09:54:36 +00:00
{
2013-01-12 04:46:01 +00:00
m_BlockDigAnimStage = 9000 ;
2012-09-25 09:54:36 +00:00
}
2013-01-12 04:46:01 +00:00
if ( m_BlockDigAnimStage / 1000 ! = lastAnimVal / 1000 )
2012-09-25 09:54:36 +00:00
{
2013-01-12 04:46:01 +00:00
m_Player - > GetWorld ( ) - > BroadcastBlockBreakAnimation ( m_UniqueID , m_BlockDigAnimX , m_BlockDigAnimY , m_BlockDigAnimZ , ( char ) ( m_BlockDigAnimStage / 1000 ) , this ) ;
2012-09-25 09:54:36 +00:00
}
}
2013-06-18 19:32:31 +00:00
2014-03-01 21:24:36 +00:00
// Reset explosion & block change counters:
2014-02-04 23:40:58 +00:00
m_NumExplosionsThisTick = 0 ;
2014-03-01 21:24:36 +00:00
m_NumBlockChangeInteractionsThisTick = 0 ;
2012-06-14 13:06:06 +00:00
}
2013-12-16 09:41:35 +00:00
void cClientHandle : : ServerTick ( float a_Dt )
{
// 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 13:06:06 +00:00
void cClientHandle : : SendAttachEntity ( const cEntity & a_Entity , const cEntity * a_Vehicle )
2012-08-17 10:18:07 +00:00
{
2013-07-07 13:06:06 +00:00
m_Protocol - > SendAttachEntity ( a_Entity , a_Vehicle ) ;
2012-08-18 09:56:28 +00:00
}
2013-07-07 13:06:06 +00: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 09:56:28 +00:00
{
2013-07-07 13:06:06 +00:00
m_Protocol - > SendBlockAction ( a_BlockX , a_BlockY , a_BlockZ , a_Byte1 , a_Byte2 , a_BlockType ) ;
2012-08-18 10:38:15 +00:00
}
2013-07-07 13:06:06 +00:00
void cClientHandle : : SendBlockBreakAnim ( int a_EntityID , int a_BlockX , int a_BlockY , int a_BlockZ , char a_Stage )
2012-08-18 10:38:15 +00:00
{
2013-07-07 13:06:06 +00:00
m_Protocol - > SendBlockBreakAnim ( a_EntityID , a_BlockX , a_BlockY , a_BlockZ , a_Stage ) ;
2012-08-17 10:18:07 +00:00
}
2013-07-07 13:06:06 +00:00
void cClientHandle : : SendBlockChange ( int a_BlockX , int a_BlockY , int a_BlockZ , BLOCKTYPE a_BlockType , NIBBLETYPE a_BlockMeta )
2012-08-19 11:51:17 +00:00
{
2013-07-07 13:06:06 +00:00
m_Protocol - > SendBlockChange ( a_BlockX , a_BlockY , a_BlockZ , a_BlockType , a_BlockMeta ) ;
2012-08-19 11:51:17 +00:00
}
2013-07-07 13:06:06 +00:00
void cClientHandle : : SendBlockChanges ( int a_ChunkX , int a_ChunkZ , const sSetBlockVector & a_Changes )
2012-08-19 11:51:17 +00:00
{
2013-08-19 19:55:03 +00:00
ASSERT ( ! a_Changes . empty ( ) ) ; // We don't want to be sending empty change packets!
2013-07-07 13:06:06 +00:00
m_Protocol - > SendBlockChanges ( a_ChunkX , a_ChunkZ , a_Changes ) ;
2012-08-19 11:51:17 +00:00
}
2014-02-15 22:16:44 +00:00
void cClientHandle : : SendChat ( const AString & a_Message , eMessageType a_ChatPrefix , const AString & a_AdditionalData )
2012-08-19 11:51:17 +00:00
{
2014-04-25 02:14:00 +00:00
bool ShouldAppendChatPrefixes = true ;
2014-02-07 18:58:52 +00: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-25 00:24:39 +00:00
AString Message = FormatMessageType ( ShouldAppendChatPrefixes , a_ChatPrefix , a_AdditionalData ) ;
2014-02-07 18:58:52 +00:00
2014-04-25 00:24:39 +00:00
m_Protocol - > SendChat ( Message . append ( a_Message ) ) ;
2012-08-19 11:51:17 +00:00
}
2014-02-15 22:16:44 +00:00
void cClientHandle : : SendChat ( const cCompositeChat & a_Message )
{
m_Protocol - > SendChat ( a_Message ) ;
}
2013-07-07 13:06:06 +00:00
void cClientHandle : : SendChunkData ( int a_ChunkX , int a_ChunkZ , cChunkDataSerializer & a_Serializer )
2012-08-19 11:51:17 +00:00
{
2013-08-20 19:13:28 +00:00
ASSERT ( m_Player ! = NULL ) ;
2013-07-07 13:06:06 +00: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 16:36:06 +00: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 11:51:17 +00:00
}
2014-06-27 18:56:29 +00:00
void cClientHandle : : SendCollectEntity ( const cEntity & a_Entity , const cPlayer & a_Player )
2012-08-19 11:51:17 +00:00
{
2014-06-27 18:56:29 +00:00
m_Protocol - > SendCollectEntity ( a_Entity , a_Player ) ;
2012-08-19 11:51:17 +00:00
}
2013-07-07 13:06:06 +00:00
void cClientHandle : : SendDestroyEntity ( const cEntity & a_Entity )
2012-08-19 11:51:17 +00:00
{
2013-07-07 13:06:06 +00:00
m_Protocol - > SendDestroyEntity ( a_Entity ) ;
2012-08-19 11:51:17 +00:00
}
2013-07-07 13:06:06 +00:00
void cClientHandle : : SendDisconnect ( const AString & a_Reason )
2012-08-19 19:42:32 +00:00
{
2013-07-07 13:06:06 +00:00
if ( ! m_HasSentDC )
{
2013-07-08 13:00:09 +00:00
LOGD ( " Sending a DC: \" %s \" " , StripColorCodes ( a_Reason ) . c_str ( ) ) ;
2013-07-07 13:06:06 +00:00
m_Protocol - > SendDisconnect ( a_Reason ) ;
m_HasSentDC = true ;
}
2012-08-19 19:42:32 +00:00
}
2013-07-29 19:42:05 +00: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 17:19:56 +00: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 13:06:06 +00:00
void cClientHandle : : SendEntityEquipment ( const cEntity & a_Entity , short a_SlotNum , const cItem & a_Item )
2012-08-19 19:42:32 +00:00
{
2013-07-07 13:06:06 +00:00
m_Protocol - > SendEntityEquipment ( a_Entity , a_SlotNum , a_Item ) ;
2012-08-19 19:42:32 +00:00
}
2013-07-07 13:06:06 +00:00
void cClientHandle : : SendEntityHeadLook ( const cEntity & a_Entity )
2012-08-19 19:42:32 +00:00
{
2013-07-07 13:06:06 +00:00
ASSERT ( a_Entity . GetUniqueID ( ) ! = m_Player - > GetUniqueID ( ) ) ; // Must not send for self
m_Protocol - > SendEntityHeadLook ( a_Entity ) ;
2012-08-19 19:42:32 +00:00
}
2013-07-07 13:06:06 +00:00
void cClientHandle : : SendEntityLook ( const cEntity & a_Entity )
2012-08-19 19:42:32 +00:00
{
ASSERT ( a_Entity . GetUniqueID ( ) ! = m_Player - > GetUniqueID ( ) ) ; // Must not send for self
2013-07-07 13:06:06 +00:00
m_Protocol - > SendEntityLook ( a_Entity ) ;
2012-08-19 19:42:32 +00:00
}
2013-07-07 13:06:06 +00:00
void cClientHandle : : SendEntityMetadata ( const cEntity & a_Entity )
2012-08-19 19:42:32 +00:00
{
2013-07-07 13:06:06 +00:00
m_Protocol - > SendEntityMetadata ( a_Entity ) ;
2012-08-19 19:42:32 +00:00
}
2013-07-07 13:06:06 +00:00
void cClientHandle : : SendEntityRelMove ( const cEntity & a_Entity , char a_RelX , char a_RelY , char a_RelZ )
2012-08-19 19:42:32 +00:00
{
ASSERT ( a_Entity . GetUniqueID ( ) ! = m_Player - > GetUniqueID ( ) ) ; // Must not send for self
2013-07-07 13:06:06 +00:00
m_Protocol - > SendEntityRelMove ( a_Entity , a_RelX , a_RelY , a_RelZ ) ;
2012-08-19 19:42:32 +00:00
}
2013-07-07 13:06:06 +00:00
void cClientHandle : : SendEntityRelMoveLook ( const cEntity & a_Entity , char a_RelX , char a_RelY , char a_RelZ )
2013-03-18 02:51:55 +00:00
{
ASSERT ( a_Entity . GetUniqueID ( ) ! = m_Player - > GetUniqueID ( ) ) ; // Must not send for self
2013-07-07 13:06:06 +00: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-18 02:51:55 +00:00
}
2013-07-07 13:06:06 +00:00
void cClientHandle : : SendEntityVelocity ( const cEntity & a_Entity )
2012-08-19 19:42:32 +00:00
{
2013-07-07 13:06:06 +00:00
m_Protocol - > SendEntityVelocity ( a_Entity ) ;
2012-08-19 19:42:32 +00:00
}
2013-07-07 13:06:06 +00: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 19:05:11 +00:00
{
2014-02-04 23:40:58 +00:00
if ( m_NumExplosionsThisTick > MAX_EXPLOSIONS_PER_TICK )
2013-07-07 13:06:06 +00:00
{
2014-02-04 23:40:58 +00:00
LOGD ( " Dropped an explosion! " ) ;
2013-07-07 13:06:06 +00:00
return ;
}
// Update the statistics:
2014-04-12 12:16:48 +00:00
m_NumExplosionsThisTick + + ;
2013-07-07 13:06:06 +00:00
m_Protocol - > SendExplosion ( a_BlockX , a_BlockY , a_BlockZ , a_Radius , a_BlocksAffected , a_PlayerMotion ) ;
2013-03-03 19:05:11 +00:00
}
2013-07-07 13:06:06 +00:00
void cClientHandle : : SendGameMode ( eGameMode a_GameMode )
2012-08-19 19:42:32 +00:00
{
2013-07-07 13:06:06 +00:00
m_Protocol - > SendGameMode ( a_GameMode ) ;
2012-08-19 19:42:32 +00:00
}
void cClientHandle : : SendHealth ( void )
{
2012-08-27 17:31:16 +00:00
m_Protocol - > SendHealth ( ) ;
2012-08-19 19:42:32 +00:00
}
2013-07-07 13:06:06 +00:00
void cClientHandle : : SendInventorySlot ( char a_WindowID , short a_SlotNum , const cItem & a_Item )
2012-08-19 19:42:32 +00:00
{
2013-07-07 13:06:06 +00:00
m_Protocol - > SendInventorySlot ( a_WindowID , a_SlotNum , a_Item ) ;
2012-08-19 19:42:32 +00:00
}
2014-02-13 15:13:09 +00: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 18:50:08 +00:00
void cClientHandle : : SendMapDecorators ( int a_ID , const cMapDecoratorList & a_Decorators )
{
m_Protocol - > SendMapDecorators ( a_ID , a_Decorators ) ;
}
2014-02-13 15:13:09 +00:00
void cClientHandle : : SendMapInfo ( int a_ID , unsigned int a_Scale )
{
m_Protocol - > SendMapInfo ( a_ID , a_Scale ) ;
}
2013-12-22 13:45:25 +00: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 13:06:06 +00:00
void cClientHandle : : SendPickupSpawn ( const cPickup & a_Pickup )
2012-08-19 19:42:32 +00:00
{
2013-07-07 13:06:06 +00:00
m_Protocol - > SendPickupSpawn ( a_Pickup ) ;
2012-08-19 19:42:32 +00:00
}
2014-02-18 00:16:03 +00:00
void cClientHandle : : SendPaintingSpawn ( const cPainting & a_Painting )
{
m_Protocol - > SendPaintingSpawn ( a_Painting ) ;
}
2012-08-19 19:42:32 +00:00
2013-12-06 23:47:07 +00:00
void cClientHandle : : SendEntityAnimation ( const cEntity & a_Entity , char a_Animation )
2012-08-19 19:42:32 +00:00
{
2013-12-06 23:47:07 +00:00
m_Protocol - > SendEntityAnimation ( a_Entity , a_Animation ) ;
2012-08-19 19:42:32 +00:00
}
2013-12-15 13:48:17 +00:00
void cClientHandle : : SendPlayerAbilities ( )
{
m_Protocol - > SendPlayerAbilities ( ) ;
}
2013-07-07 13:06:06 +00:00
void cClientHandle : : SendPlayerListItem ( const cPlayer & a_Player , bool a_IsOnline )
2012-08-19 21:14:45 +00:00
{
2013-07-07 13:06:06 +00:00
m_Protocol - > SendPlayerListItem ( a_Player , a_IsOnline ) ;
2012-08-19 21:14:45 +00:00
}
2013-07-07 13:06:06 +00:00
void cClientHandle : : SendPlayerMaxSpeed ( void )
2013-04-18 02:42:45 +00:00
{
2013-07-07 13:06:06 +00:00
m_Protocol - > SendPlayerMaxSpeed ( ) ;
2013-04-18 02:42:45 +00:00
}
2013-07-07 13:06:06 +00:00
void cClientHandle : : SendPlayerMoveLook ( void )
2012-08-19 21:14:45 +00:00
{
2013-07-07 13:06:06 +00: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 21:14:45 +00:00
}
2013-07-07 13:06:06 +00:00
void cClientHandle : : SendPlayerPosition ( void )
2012-08-19 21:14:45 +00:00
{
2013-07-07 13:06:06 +00:00
m_Protocol - > SendPlayerPosition ( ) ;
2012-08-19 21:14:45 +00:00
}
2012-08-24 07:58:26 +00:00
void cClientHandle : : SendPlayerSpawn ( const cPlayer & a_Player )
{
2012-08-27 17:31:16 +00:00
if ( a_Player . GetUniqueID ( ) = = m_Player - > GetUniqueID ( ) )
{
// Do NOT send this packet to myself
return ;
}
2014-07-17 14:33:09 +00:00
LOGD ( " Spawning player \" %s \" on client \" %s \" @ %s " ,
2012-09-23 16:25:32 +00:00
a_Player . GetName ( ) . c_str ( ) , GetPlayer ( ) - > GetName ( ) . c_str ( ) , GetIPString ( ) . c_str ( )
) ;
2012-08-27 17:31:16 +00:00
m_Protocol - > SendPlayerSpawn ( a_Player ) ;
2012-08-24 07:58:26 +00:00
}
2014-01-09 10:39:42 +00:00
void cClientHandle : : SendPluginMessage ( const AString & a_Channel , const AString & a_Message )
{
m_Protocol - > SendPluginMessage ( a_Channel , a_Message ) ;
}
2013-12-14 17:19:56 +00:00
void cClientHandle : : SendRemoveEntityEffect ( const cEntity & a_Entity , int a_EffectID )
{
m_Protocol - > SendRemoveEntityEffect ( a_Entity , a_EffectID ) ;
}
2014-07-18 19:12:27 +00:00
void cClientHandle : : SendRespawn ( eDimension a_Dimension , bool a_ShouldIgnoreDimensionChecks )
2012-08-24 07:58:26 +00:00
{
2014-07-18 19:12:27 +00:00
m_Protocol - > SendRespawn ( a_Dimension , a_ShouldIgnoreDimensionChecks ) ;
2012-08-24 07:58:26 +00:00
}
2013-11-15 15:23:50 +00:00
void cClientHandle : : SendExperience ( void )
2013-11-15 11:42:09 +00:00
{
2013-11-15 15:23:50 +00:00
m_Protocol - > SendExperience ( ) ;
2013-11-15 11:42:09 +00:00
}
2013-11-25 20:43:43 +00:00
void cClientHandle : : SendExperienceOrb ( const cExpOrb & a_ExpOrb )
2013-11-25 19:04:39 +00:00
{
2013-11-25 20:43:43 +00:00
m_Protocol - > SendExperienceOrb ( a_ExpOrb ) ;
2013-11-25 19:04:39 +00:00
}
2014-01-21 13:58:17 +00: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 ) ;
}
2014-07-13 00:08:02 +00:00
void cClientHandle : : SendSoundEffect ( const AString & a_SoundName , double a_X , double a_Y , double a_Z , float a_Volume , float a_Pitch )
2012-12-26 09:12:00 +00:00
{
2014-07-13 00:08:02 +00:00
m_Protocol - > SendSoundEffect ( a_SoundName , a_X , a_Y , a_Z , a_Volume , a_Pitch ) ;
2012-12-26 09:12:00 +00:00
}
2013-07-07 13:06:06 +00:00
void cClientHandle : : SendSoundParticleEffect ( int a_EffectID , int a_SrcX , int a_SrcY , int a_SrcZ , int a_Data )
2012-08-24 07:58:26 +00:00
{
2013-07-07 13:06:06 +00:00
m_Protocol - > SendSoundParticleEffect ( a_EffectID , a_SrcX , a_SrcY , a_SrcZ , a_Data ) ;
2012-08-24 07:58:26 +00:00
}
2013-07-07 13:06:06 +00:00
void cClientHandle : : SendSpawnFallingBlock ( const cFallingBlock & a_FallingBlock )
2012-08-24 07:58:26 +00:00
{
2013-07-07 13:06:06 +00:00
m_Protocol - > SendSpawnFallingBlock ( a_FallingBlock ) ;
2012-08-24 07:58:26 +00:00
}
2013-07-07 13:06:06 +00:00
void cClientHandle : : SendSpawnMob ( const cMonster & a_Mob )
2012-08-24 09:49:00 +00:00
{
2013-07-07 13:06:06 +00:00
m_Protocol - > SendSpawnMob ( a_Mob ) ;
2012-08-24 09:49:00 +00:00
}
2013-07-07 13:06:06 +00:00
void cClientHandle : : SendSpawnObject ( const cEntity & a_Entity , char a_ObjectType , int a_ObjectData , Byte a_Yaw , Byte a_Pitch )
2012-08-25 17:52:08 +00:00
{
2013-07-07 13:06:06 +00:00
m_Protocol - > SendSpawnObject ( a_Entity , a_ObjectType , a_ObjectData , a_Yaw , a_Pitch ) ;
2012-08-25 17:52:08 +00:00
}
2014-07-17 17:13:23 +00:00
void cClientHandle : : SendSpawnVehicle ( const cEntity & a_Vehicle , char a_VehicleType , char a_VehicleSubType ) // VehicleSubType is specific to Minecarts
2012-08-25 17:52:08 +00:00
{
2013-08-29 12:47:22 +00:00
m_Protocol - > SendSpawnVehicle ( a_Vehicle , a_VehicleType , a_VehicleSubType ) ;
2012-08-25 17:52:08 +00:00
}
2014-05-11 11:57:06 +00:00
void cClientHandle : : SendStatistics ( const cStatManager & a_Manager )
{
m_Protocol - > SendStatistics ( a_Manager ) ;
}
2013-07-30 20:48:59 +00:00
void cClientHandle : : SendTabCompletionResults ( const AStringVector & a_Results )
{
m_Protocol - > SendTabCompletionResults ( a_Results ) ;
}
2013-07-07 13:06:06 +00:00
void cClientHandle : : SendTeleportEntity ( const cEntity & a_Entity )
2012-08-25 17:52:08 +00:00
{
2013-07-07 13:06:06 +00:00
m_Protocol - > SendTeleportEntity ( a_Entity ) ;
2012-08-25 17:52:08 +00:00
}
2013-07-07 13:06:06 +00:00
void cClientHandle : : SendThunderbolt ( int a_BlockX , int a_BlockY , int a_BlockZ )
2012-08-25 21:46:18 +00:00
{
2013-07-07 13:06:06 +00:00
m_Protocol - > SendThunderbolt ( a_BlockX , a_BlockY , a_BlockZ ) ;
2012-08-25 21:46:18 +00:00
}
2012-11-01 21:38:20 +00:00
void cClientHandle : : SendTimeUpdate ( Int64 a_WorldAge , Int64 a_TimeOfDay )
2012-08-25 21:46:18 +00:00
{
2012-11-01 21:38:20 +00:00
m_Protocol - > SendTimeUpdate ( a_WorldAge , a_TimeOfDay ) ;
2012-08-25 21:46:18 +00:00
}
2013-07-07 13:06:06 +00:00
void cClientHandle : : SendUnloadChunk ( int a_ChunkX , int a_ChunkZ )
2012-08-25 21:46:18 +00:00
{
2013-07-07 13:06:06 +00:00
m_Protocol - > SendUnloadChunk ( a_ChunkX , a_ChunkZ ) ;
2012-08-25 21:46:18 +00:00
}
2014-01-19 19:42:25 +00:00
void cClientHandle : : SendUpdateBlockEntity ( cBlockEntity & a_BlockEntity )
2014-01-19 00:54:38 +00:00
{
2014-01-19 19:42:25 +00:00
m_Protocol - > SendUpdateBlockEntity ( a_BlockEntity ) ;
2014-01-19 00:54:38 +00:00
}
2012-08-25 21:46:18 +00:00
2013-07-07 13:06:06 +00: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 12:01:34 +00:00
{
2013-07-07 13:06:06 +00:00
m_Protocol - > SendUpdateSign (
a_BlockX , a_BlockY , a_BlockZ ,
a_Line1 , a_Line2 , a_Line3 , a_Line4
) ;
2012-09-11 12:01:34 +00:00
}
2013-07-07 13:06:06 +00:00
void cClientHandle : : SendUseBed ( const cEntity & a_Entity , int a_BlockX , int a_BlockY , int a_BlockZ )
2012-10-21 07:46:28 +00:00
{
2013-07-07 13:06:06 +00:00
m_Protocol - > SendUseBed ( a_Entity , a_BlockX , a_BlockY , a_BlockZ ) ;
2012-10-21 07:46:28 +00:00
}
2013-07-07 13:06:06 +00:00
void cClientHandle : : SendWeather ( eWeather a_Weather )
2012-09-25 09:54:36 +00:00
{
2013-07-07 13:06:06 +00:00
m_Protocol - > SendWeather ( a_Weather ) ;
2012-09-25 09:54:36 +00:00
}
2013-08-18 11:26:37 +00:00
void cClientHandle : : SendWholeInventory ( const cWindow & a_Window )
2012-09-30 16:37:44 +00:00
{
2013-08-18 11:26:37 +00:00
m_Protocol - > SendWholeInventory ( a_Window ) ;
2012-09-30 16:37:44 +00:00
}
2012-09-29 20:43:42 +00:00
2013-08-18 11:26:37 +00:00
void cClientHandle : : SendWindowClose ( const cWindow & a_Window )
2013-02-18 16:48:50 +00:00
{
2013-08-18 11:26:37 +00:00
m_Protocol - > SendWindowClose ( a_Window ) ;
2013-02-18 16:48:50 +00:00
}
2013-11-08 20:32:14 +00:00
void cClientHandle : : SendWindowOpen ( const cWindow & a_Window )
2012-08-26 21:01:07 +00:00
{
2013-11-08 20:32:14 +00:00
m_Protocol - > SendWindowOpen ( a_Window ) ;
2013-08-18 11:26:37 +00: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 21:01:07 +00:00
}
2012-06-14 13:06:06 +00:00
const AString & cClientHandle : : GetUsername ( void ) const
{
return m_Username ;
}
2012-10-18 21:54:56 +00:00
void cClientHandle : : SetUsername ( const AString & a_Username )
{
m_Username = a_Username ;
}
2012-06-14 13:06:06 +00: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 22:24:39 +00:00
bool cClientHandle : : HasPluginChannel ( const AString & a_PluginChannel )
{
return ( m_PluginChannels . find ( a_PluginChannel ) ! = m_PluginChannels . end ( ) ) ;
}
2012-06-14 13:06:06 +00:00
bool cClientHandle : : WantsSendChunk ( int a_ChunkX , int a_ChunkY , int a_ChunkZ )
{
2012-11-11 14:00:58 +00:00
if ( m_State > = csDestroying )
{
return false ;
}
2012-06-14 13:06:06 +00: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 14:00:58 +00:00
if ( m_State > = csDestroying )
{
return ;
}
2012-06-14 13:06:06 +00: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 17:31:16 +00:00
void cClientHandle : : PacketBufferFull ( void )
2012-06-14 13:06:06 +00:00
{
2012-08-27 17:31:16 +00:00
// Too much data in the incoming queue, the server is probably too busy, kick the client:
2012-09-25 08:23:19 +00:00
LOGERROR ( " Too much data in queue for client \" %s \" @ %s, kicking them. " , m_Username . c_str ( ) , m_IPString . c_str ( ) ) ;
2012-08-27 17:31:16 +00:00
SendDisconnect ( " Server busy " ) ;
Destroy ( ) ;
}
2012-06-14 13:06:06 +00:00
2012-08-27 17:31:16 +00:00
2013-12-12 22:05:23 +00:00
void cClientHandle : : PacketUnknown ( UInt32 a_PacketType )
2012-08-27 17:31:16 +00:00
{
2013-12-12 22:05:23 +00:00
LOGERROR ( " Unknown packet type 0x%x from client \" %s \" @ %s " , a_PacketType , m_Username . c_str ( ) , m_IPString . c_str ( ) ) ;
2012-08-27 17:31:16 +00:00
AString Reason ;
2013-12-12 22:05:23 +00:00
Printf ( Reason , " Unknown [C->S] PacketType: 0x%x " , a_PacketType ) ;
2012-08-27 17:31:16 +00:00
SendDisconnect ( Reason ) ;
Destroy ( ) ;
2012-06-14 13:06:06 +00:00
}
2012-08-27 17:31:16 +00:00
void cClientHandle : : PacketError ( unsigned char a_PacketType )
2012-06-14 13:06:06 +00:00
{
2012-08-27 17:31:16 +00: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 13:06:06 +00:00
2012-08-27 17:31:16 +00:00
2014-05-01 13:08:15 +00:00
bool cClientHandle : : DataReceived ( const char * a_Data , size_t a_Size )
2012-08-27 17:31:16 +00:00
{
2013-08-13 20:45:29 +00:00
// Data is received from the client, store it in the buffer to be processed by the Tick thread:
2012-11-01 21:38:20 +00:00
m_TimeSinceLastPacket = 0 ;
2013-08-13 20:45:29 +00:00
cCSLock Lock ( m_CSIncomingData ) ;
m_IncomingData . append ( a_Data , a_Size ) ;
2014-05-01 13:08:15 +00:00
return false ;
2012-08-27 17:31:16 +00:00
}
void cClientHandle : : GetOutgoingData ( AString & a_Data )
{
// Data can be sent to client
2012-09-04 19:05:35 +00:00
{
cCSLock Lock ( m_CSOutgoingData ) ;
m_OutgoingData . ReadAll ( a_Data ) ;
m_OutgoingData . CommitRead ( ) ;
2012-09-06 08:33:43 +00:00
a_Data . append ( m_OutgoingDataOverflow ) ;
m_OutgoingDataOverflow . clear ( ) ;
2012-09-04 19:05:35 +00:00
}
2012-06-14 13:06:06 +00:00
// Disconnect player after all packets have been sent
2013-01-05 19:05:15 +00:00
if ( m_HasSentDC & & a_Data . empty ( ) )
2012-06-14 13:06:06 +00:00
{
Destroy ( ) ;
}
}
void cClientHandle : : SocketClosed ( void )
{
// The socket has been closed for any reason
2013-12-18 18:17:17 +00:00
LOGD ( " Player %s @ %s disconnected " , m_Username . c_str ( ) , m_IPString . c_str ( ) ) ;
2014-05-02 09:18:02 +00:00
if ( ! m_Username . empty ( ) ) // Ignore client pings
2013-12-18 18:17:17 +00:00
{
2014-05-02 09:18:02 +00:00
cRoot : : Get ( ) - > GetPluginManager ( ) - > CallHookDisconnect ( * this , " Player disconnected " ) ;
2013-12-18 18:17:17 +00:00
}
2012-09-25 08:23:19 +00:00
Destroy ( ) ;
2012-06-14 13:06:06 +00:00
}
2012-06-14 20:06:25 +00:00
2014-04-16 12:33:03 +00: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 17:31:43 +00:00
if ( Item . EnchantByXPLevels ( BaseEnchantmentLevel ) )
2014-04-16 12:33:03 +00:00
{
2014-04-18 12:10:31 +00:00
if ( m_Player - > IsGameModeCreative ( ) | | m_Player - > DeltaExperience ( - m_Player - > XpForLevel ( BaseEnchantmentLevel ) ) > = 0 )
2014-04-17 17:31:43 +00:00
{
Window - > m_SlotArea - > SetSlot ( 0 , * m_Player , Item ) ;
Window - > SendSlot ( * m_Player , Window - > m_SlotArea , 0 ) ;
Window - > BroadcastWholeWindow ( ) ;
2014-04-16 12:33:03 +00:00
2014-04-17 17:31:43 +00:00
Window - > SetProperty ( 0 , 0 , * m_Player ) ;
Window - > SetProperty ( 1 , 0 , * m_Player ) ;
Window - > SetProperty ( 2 , 0 , * m_Player ) ;
}
2014-04-16 12:33:03 +00:00
}
}