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 11:39:13 +02:00
#include "Entities/Pickup.h"
2013-12-08 12:17:54 +01:00
#include "Bindings/PluginManager.h"
2013-08-19 11:39:13 +02: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 19:58:46 +02: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 16:11:19 +02:00
#include "Blocks/BlockSlab.h"
2014-01-26 06:20:39 -08: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 13:04:56 +02: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 23:16:44 +01:00
#include "CompositeChat.h"
2014-02-23 20:02:44 +01:00
#include "Items/ItemSword.h"
2012-06-14 13:06:06 +00:00
2014-04-14 20:21:00 +02:00
#include "md5/md5.h"
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
2014-02-04 23:40:58 +00:00
/** How many ticks before the socket is closed after the client is destroyed (#31) */
2013-08-20 22:27:29 +02:00
static const int TICKS_BEFORE_CLOSE = 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 ;
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cClientHandle:
2013-12-20 19:10:07 +01:00
cClientHandle :: cClientHandle ( const cSocket * a_Socket , int a_ViewDistance ) :
m_ViewDistance ( a_ViewDistance ),
m_IPString ( a_Socket -> GetIPString ()),
m_OutgoingData ( 64 KiB ),
m_Player ( NULL ),
m_HasSentDC ( false ),
m_LastStreamedChunkX ( 0x7fffffff ), // bogus chunk coords to force streaming upon login
m_LastStreamedChunkZ ( 0x7fffffff ),
m_TimeSinceLastPacket ( 0 ),
m_Ping ( 1000 ),
m_PingID ( 1 ),
m_BlockDigAnimStage ( - 1 ),
m_HasStartedDigging ( false ),
m_TicksSinceDestruction ( 0 ),
m_State ( csConnected ),
m_ShouldCheckDownloaded ( false ),
2014-02-04 23:40:58 +00:00
m_NumExplosionsThisTick ( 0 ),
2013-12-20 19:10:07 +01:00
m_UniqueID ( 0 ),
2014-03-08 08:33:38 -08: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
2012-06-14 13:06:06 +00:00
s_ClientCount ++ ; // Not protected by CS because clients are always constructed from the same thread
m_UniqueID = s_ClientCount ;
cTimer t1 ;
m_LastPingTime = t1 . GetNowTime ();
2012-08-27 17:31:16 +00:00
LOGD ( "New ClientHandle created at %p" , this );
2012-06-14 13:06:06 +00:00
}
cClientHandle ::~ cClientHandle ()
{
2013-08-20 22:27:29 +02:00
ASSERT ( m_State >= csDestroyedWaiting ); // 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 )
{
World -> RemovePlayer ( m_Player );
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 19:31:43 +01: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 22:45:29 +02:00
void cClientHandle :: Destroy ( void )
2012-06-14 13:06:06 +00:00
{
2013-08-13 22:45:29 +02: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 );
}
2013-08-20 22:27:29 +02:00
m_State = csDestroyedWaiting ;
2012-06-14 13:06:06 +00:00
}
2014-04-14 20:21:00 +02:00
void cClientHandle :: GenerateOfflineUUID ( void )
2014-04-14 22:52:59 +02:00
{
m_UUID = GenerateOfflineUUID ( m_Username );
}
2014-04-26 09:21:49 -07:00
AString cClientHandle :: FormatChatPrefix ( bool ShouldAppendChatPrefixes , AString a_ChatPrefixS , AString m_Color1 , AString m_Color2 )
{
if ( ShouldAppendChatPrefixes )
2014-04-26 14:01:48 -07:00
return Printf ( "%s[%s] %s" , m_Color1 . c_str (), a_ChatPrefixS . c_str (), m_Color2 . c_str ());
2014-04-26 09:21:49 -07:00
else
return Printf ( "%s" , m_Color1 . c_str ());
}
2014-04-24 17:24:39 -07:00
AString cClientHandle :: FormatMessageType ( bool ShouldAppendChatPrefixes , eMessageType a_ChatPrefix , const AString & a_AdditionalData )
{
switch ( a_ChatPrefix )
{
2014-04-27 17:35:41 +01:00
case mtCustom : return "" ;
2014-04-26 14:01:48 -07:00
case mtFailure : return FormatChatPrefix ( ShouldAppendChatPrefixes , "INFO" , cChatColor :: Rose , cChatColor :: White );
case mtInformation : return FormatChatPrefix ( ShouldAppendChatPrefixes , "INFO" , cChatColor :: Yellow , cChatColor :: White );
case mtSuccess : return FormatChatPrefix ( ShouldAppendChatPrefixes , "INFO" , cChatColor :: Green , cChatColor :: White );
case mtWarning : return FormatChatPrefix ( ShouldAppendChatPrefixes , "WARN" , cChatColor :: Rose , cChatColor :: White );
case mtFatal : return FormatChatPrefix ( ShouldAppendChatPrefixes , "FATAL" , cChatColor :: Red , cChatColor :: White );
case mtDeath : return FormatChatPrefix ( ShouldAppendChatPrefixes , "DEATH" , cChatColor :: Gray , cChatColor :: White );
case mtJoin : return FormatChatPrefix ( ShouldAppendChatPrefixes , "JOIN" , cChatColor :: Yellow , cChatColor :: White );
case mtLeave : return FormatChatPrefix ( ShouldAppendChatPrefixes , "LEAVE" , cChatColor :: Yellow , cChatColor :: White );
2014-04-24 17:24:39 -07:00
case mtPrivateMessage :
{
if ( ShouldAppendChatPrefixes )
2014-04-28 20:37:22 +02:00
{
2014-04-24 17:24:39 -07: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 20:37:22 +02:00
}
2014-04-24 17:24:39 -07:00
else
2014-04-28 20:37:22 +02:00
{
2014-04-24 17:24:39 -07:00
return Printf ( "%s: %s" , a_AdditionalData . c_str (), cChatColor :: LightBlue . c_str ());
2014-04-28 20:37:22 +02:00
}
2014-04-24 17:24:39 -07:00
}
}
ASSERT ( ! "Unhandled chat prefix type!" );
2014-04-27 17:35:41 +01:00
return "" ;
2014-04-24 17:24:39 -07:00
}
2014-04-14 22:52:59 +02:00
AString cClientHandle :: GenerateOfflineUUID ( const AString & a_Username )
2014-04-14 20:21:00 +02:00
{
// Proper format for a version 3 UUID is:
// xxxxxxxx-xxxx-3xxx-yxxx-xxxxxxxxxxxx where x is any hexadecimal digit and y is one of 8, 9, A, or B
// Generate an md5 checksum, and use it as base for the ID:
2014-04-14 22:52:59 +02:00
MD5 Checksum ( a_Username );
AString UUID = Checksum . hexdigest ();
UUID [ 12 ] = '3' ; // Version 3 UUID
UUID [ 16 ] = '8' ; // Variant 1 UUID
2014-04-14 20:21:00 +02:00
// Now the digest doesn't have the UUID slashes, but the client requires them, so add them into the appropriate positions:
2014-04-14 22:52:59 +02:00
UUID . insert ( 8 , "-" );
UUID . insert ( 13 , "-" );
UUID . insert ( 18 , "-" );
UUID . insert ( 23 , "-" );
return UUID ;
2014-04-14 20:21:00 +02:00
}
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-04-13 13:04:56 +02:00
void cClientHandle :: Authenticate ( const AString & a_Name , const AString & a_UUID )
2012-06-14 13:06:06 +00:00
{
if ( m_State != csAuthenticating )
{
return ;
}
ASSERT ( m_Player == NULL );
2014-04-13 13:04:56 +02:00
m_Username = a_Name ;
m_UUID = a_UUID ;
2014-04-14 22:52:59 +02:00
// Send login success (if the protocol supports it):
m_Protocol -> SendLoginSuccess ();
2014-04-13 13:04:56 +02: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-02-03 21:12:44 +00:00
LOGINFO ( "Player %s has joined the game." , m_Username . c_str ());
}
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 18:36:58 +01: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-16 02:23:50 +11:00
// Send experience
m_Player -> SendExperience ();
2012-06-14 13:06:06 +00:00
m_Player -> Initialize ( World );
2013-08-13 22:45:29 +02:00
m_State = csAuthenticated ;
2014-01-21 15:58:17 +02:00
// Query player team
m_Player -> UpdateTeam ();
2014-01-21 19:43:13 +02:00
// Send scoreboard data
World -> GetScoreBoard (). SendTo ( * this );
2014-05-11 14:57:06 +03:00
2014-04-29 23:10:50 +02: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 19:43:13 +02: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 10:24:34 +02:00
if (( m_State < csAuthenticated ) || ( m_State >= csDestroying ))
2012-06-14 13:06:06 +00:00
{
return ;
}
ASSERT ( m_Player != NULL );
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 17:53:02 +02:00
const cServer & Server = * cRoot :: Get () -> GetServer ();
2014-04-18 21:09:44 +02:00
2012-06-14 13:06:06 +00:00
Printf ( Reply , "%s%s%i%s%i" ,
2014-04-19 17:53:02 +02:00
Server . GetDescription (). c_str (),
2013-08-11 19:18:06 +02:00
cChatColor :: Delimiter . c_str (),
2014-04-19 17:53:02 +02:00
Server . GetNumPlayers (),
2013-08-11 19:18:06 +02:00
cChatColor :: Delimiter . c_str (),
2014-04-19 17:53:02 +02:00
Server . GetMaxPlayers ()
2012-06-14 13:06:06 +00:00
);
2014-04-18 21:09:44 +02: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 21:19:06 +01: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 13:30:24 +01: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 15:11:59 +01:00
void cClientHandle :: HandlePlayerAbilities ( bool a_CanFly , bool a_IsFlying , float FlyingSpeed , float WalkingSpeed )
2013-12-15 14:48:17 +01:00
{
2013-12-19 20:53:47 +00:00
UNUSED ( FlyingSpeed ); // Ignore the client values for these
UNUSED ( WalkingSpeed );
2013-12-15 15:11:59 +01:00
m_Player -> SetCanFly ( a_CanFly );
m_Player -> SetFlying ( a_IsFlying );
2013-12-15 14:48:17 +01: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 18:36:06 +02: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 16:38:14 +02: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 16:38:14 +02:00
LOGD("Invalid stance");
SendPlayerMoveLook();
2014-05-16 22:04:19 +02:00
return;
}
2014-05-26 16:38:14 +02:00
*/
2013-09-08 18:36:06 +02: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 16:57:34 +01:00
// we only add this exhaustion if the player is not swimming - otherwise we end up with both jump + swim exhaustion
2013-08-09 08:37:39 +01:00
2013-09-08 18:36:06 +02:00
if ( ! m_Player -> IsSwimming ())
2013-08-08 16:57:34 +01: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 17:47:05 +01:00
void cClientHandle :: HandlePluginMessage ( const AString & a_Channel , const AString & a_Message )
{
2014-02-20 23:24:39 +01:00
if ( a_Channel == "MC|AdvCdm" )
2014-01-18 19:58:46 +02:00
{
2014-02-20 23:24:39 +01: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 23:24:39 +01:00
else if ( a_Channel == "MC|Brand" )
2014-01-19 01:06:19 +00:00
{
2014-02-20 23:24:39 +01:00
// Client <-> Server branding exchange
2014-01-19 01:06:19 +00:00
SendPluginMessage ( "MC|Brand" , "MCServer" );
2014-02-20 23:24:39 +01:00
}
2014-05-01 01:25:04 +02:00
else if ( a_Channel == "MC|ItemName" )
{
HandleAnvilItemName ( a_Message . c_str (), a_Message . size ());
}
2014-02-20 23:24:39 +01:00
else if ( a_Channel == "REGISTER" )
{
2014-03-06 20:06:53 +00:00
if ( HasPluginChannel ( a_Channel ))
{
SendPluginMessage ( "UNREGISTER" , a_Channel );
return ; // Can't register again if already taken - kinda defeats the point of plugin messaging!
}
2014-02-20 23:24:39 +01:00
RegisterPluginChannels ( BreakApartPluginChannels ( a_Message ));
}
else if ( a_Channel == "UNREGISTER" )
{
UnregisterPluginChannels ( BreakApartPluginChannels ( a_Message ));
2014-01-18 19:58:46 +02: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 19:58:46 +02:00
2014-01-07 17:47:05 +01:00
cPluginManager :: Get () -> CallHookPluginMessage ( * this , a_Channel , a_Message );
}
2014-02-20 23:24:39 +01: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 23:03:23 +02:00
void cClientHandle :: HandleCommandBlockMessage ( const char * a_Data , size_t a_Length )
2014-01-18 19:58:46 +02: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 19:58:46 +02:00
LOGD ( "Malformed MC|AdvCdm packet." );
return ;
}
2014-01-18 21:27:54 +02:00
cByteBuffer Buffer ( a_Length );
Buffer . Write ( a_Data , a_Length );
2014-01-18 19:58:46 +02:00
int BlockX , BlockY , BlockZ ;
AString Command ;
2014-01-18 21:27:54 +02:00
char Mode ;
Buffer . ReadChar ( Mode );
switch ( Mode )
2014-01-18 19:58:46 +02:00
{
case 0x00 :
{
2014-01-18 21:27:54 +02:00
Buffer . ReadBEInt ( BlockX );
Buffer . ReadBEInt ( BlockY );
Buffer . ReadBEInt ( BlockZ );
2014-01-18 19:58:46 +02:00
2014-01-18 21:27:54 +02:00
Buffer . ReadVarUTF8String ( Command );
2014-01-18 19:58:46 +02:00
break ;
}
default :
{
2014-02-07 18:58:52 +00:00
SendChat ( "Failure setting command block command; unhandled mode" , mtFailure );
2014-01-18 19:58:46 +02:00
LOGD ( "Unhandled MC|AdvCdm packet mode." );
return ;
}
}
cWorld * World = m_Player -> GetWorld ();
2014-01-23 14:57:04 +02: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 14:57:04 +02:00
}
else
{
2014-02-07 18:58:52 +00:00
SendChat ( "Command blocks are not enabled on this server" , mtFailure );
2014-01-23 14:57:04 +02:00
}
2014-01-18 19:58:46 +02:00
}
2014-05-05 15:42:41 +02:00
void cClientHandle :: HandleAnvilItemName ( const char * a_Data , size_t a_Length )
2014-05-01 01:25:04 +02:00
{
if ( a_Length < 1 )
{
return ;
}
if (( m_Player -> GetWindow () == NULL ) || ( m_Player -> GetWindow () -> GetWindowType () != cWindow :: wtAnvil ))
{
return ;
}
2014-05-06 17:31:02 +02:00
AString Name ( a_Data , a_Length );
2014-05-01 01:25:04 +02:00
if ( Name . length () <= 30 )
{
2014-05-06 17:31:02 +02:00
(( cAnvilWindow * ) m_Player -> GetWindow ()) -> SetRepairedItemName ( Name , m_Player );
2014-05-01 01:25:04 +02:00
}
}
2014-02-04 10:59:05 -08: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 19:58:48 +02:00
if (
2014-05-18 22:41:42 +01:00
(( a_Status == DIG_STATUS_STARTED ) || ( a_Status == DIG_STATUS_FINISHED )) && // Only do a radius check for block destruction - things like pickup tossing send coordinates that are to be ignored
(( Diff ( m_Player -> GetPosX (), ( double ) a_BlockX ) > 6 ) ||
2014-05-15 19:58:48 +02:00
( Diff ( m_Player -> GetPosY (), ( double ) a_BlockY ) > 6 ) ||
2014-05-18 22:41:42 +01:00
( Diff ( m_Player -> GetPosZ (), ( double ) a_BlockZ ) > 6 ))
2014-05-15 19:58:48 +02: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 00:27:39 -07: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 ());
if ( ItemHandler -> IsFood ())
{
2013-07-28 19:15:03 +02: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 14:24:03 +02: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 11:56:15 +02: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-01-23 00:27:39 -07: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 10:59:05 -08: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 23:10:02 +02:00
2014-02-23 20:02:44 +01:00
if (
m_Player -> IsGameModeCreative () &&
2014-02-23 20:31:58 +01:00
ItemCategory :: IsSword ( m_Player -> GetInventory (). GetEquippedItem (). m_ItemType )
2014-02-23 20:02:44 +01:00
)
{
// Players can't destroy blocks with a Sword in the hand.
return ;
}
2014-05-09 23:10:02 +02:00
2014-05-15 19:58:48 +02: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 19:58:48 +02: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
2013-02-14 21:47:57 +00:00
if (
2013-10-29 21:45:31 +01:00
( m_Player -> IsGameModeCreative ()) || // In creative mode, digging is done immediately
2014-03-01 21:34:19 +02: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 08:35:48 -08:00
cChunkInterface ChunkInterface ( World -> GetChunkMap ());
2014-03-02 21:25:05 +02:00
cBlockHandler * Handler = cBlockInfo :: GetHandler ( a_OldBlock );
2014-02-01 08:35:48 -08: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
// Check for clickthrough-blocks:
if ( a_BlockFace != BLOCK_FACE_NONE )
{
int pX = a_BlockX ;
int pY = a_BlockY ;
int pZ = a_BlockZ ;
2013-12-06 19:22:25 +00:00
AddFaceDirection ( pX , pY , pZ , a_BlockFace ); // Get the block in front of the clicked coordinates (m_bInverse defaulted to false)
2014-03-02 21:25:05 +02:00
Handler = cBlockInfo :: GetHandler ( World -> GetBlock ( pX , pY , pZ ));
2013-12-06 19:22:25 +00:00
2013-01-12 04:46:01 +00:00
if ( Handler -> IsClickedThrough ())
{
2014-02-01 08:35:48 -08:00
Handler -> OnDigging ( ChunkInterface , * World , m_Player , pX , pY , pZ );
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 10:59:05 -08: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 14:16:08 -07: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 11:56:15 +02:00
FinishDigAnimation ();
2012-09-25 09:54:36 +00:00
2014-05-15 19:58:48 +02:00
cWorld * World = m_Player -> GetWorld ();
2013-01-12 04:46:01 +00:00
cItemHandler * ItemHandler = cItemHandler :: GetItemHandler ( m_Player -> GetEquippedItem ());
2014-05-09 23:10:02 +02: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 23:10:02 +02: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 23:43:00 +02: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 18:08:05 +02:00
// The ItemHandler is also responsible for spawning the pickups
2014-02-01 06:01:13 -08: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 11:56:15 +02:00
void cClientHandle :: FinishDigAnimation ()
2014-05-09 23:10:02 +02:00
{
if (
! m_HasStartedDigging || // Hasn't received the DIG_STARTED packet
( m_LastDigBlockX == - 1 ) ||
( m_LastDigBlockY == - 1 ) ||
( m_LastDigBlockZ == - 1 )
)
{
return ;
}
m_HasStartedDigging = false ;
if ( m_BlockDigAnimStage != - 1 )
{
// End dig animation
m_BlockDigAnimStage = - 1 ;
// It seems that 10 ends block animation
m_Player -> GetWorld () -> BroadcastBlockBreakAnimation ( m_UniqueID , m_LastDigBlockX , m_LastDigBlockY , m_LastDigBlockZ , 10 , this );
}
m_BlockDigAnimX = - 1 ;
m_BlockDigAnimY = - 1 ;
m_BlockDigAnimZ = - 1 ;
}
2014-02-04 10:59:05 -08: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 15:10:20 +01:00
cWorld * World = m_Player -> GetWorld ();
2014-05-15 19:58:48 +02: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 )
)
{
if ( a_BlockFace != BLOCK_FACE_NONE )
{
AddFaceDirection ( a_BlockX , a_BlockY , a_BlockZ , a_BlockFace );
World -> SendBlockTo ( a_BlockX , a_BlockY , a_BlockZ , m_Player );
World -> SendBlockTo ( a_BlockX , a_BlockY + 1 , a_BlockZ , m_Player ); // 2 block high things
m_Player -> GetInventory (). SendEquippedSlot ();
}
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 15:10:20 +01: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 15:10:20 +01:00
World -> SendBlockTo ( a_BlockX , a_BlockY , a_BlockZ , m_Player );
2014-05-15 19:58:48 +02:00
World -> SendBlockTo ( a_BlockX , a_BlockY + 1 , a_BlockZ , m_Player ); // 2 block high things
2014-05-09 23:10:02 +02: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)
// The -1 check is there because sometimes the client sends -1 instead of the held item
// ( http://forum.mc-server.org/showthread.php?tid=549&pid=4502#pid4502 )
2012-06-14 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 15:10:20 +01: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 21:25:05 +02:00
cBlockHandler * BlockHandler = cBlockInfo :: GetHandler ( BlockType );
2012-12-19 21:19:36 +00:00
2013-08-30 14:24:03 +02: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 05:06:32 -08: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
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-04-24 20:41:25 +02:00
else if ( ItemHandler -> IsFood () && ! m_Player -> IsGameModeCreative ())
2013-01-12 04:46:01 +00:00
{
2013-07-28 22:55:09 +02:00
if ( m_Player -> IsSatiated ())
{
// The player is satiated, they cannot eat
return ;
}
2013-07-28 19:15:03 +02:00
m_Player -> StartEating ();
if ( PlgMgr -> CallHookPlayerEating ( * m_Player ))
2012-06-14 13:06:06 +00:00
{
2013-07-28 19:15:03 +02:00
// A plugin won't let us eat, abort (send the proper packets to the client, too):
m_Player -> AbortEating ();
2013-01-12 04:46:01 +00:00
return ;
2012-08-18 09:56:28 +00:00
}
2013-07-28 19:15:03 +02:00
return ;
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 10:59:05 -08: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 19:38:39 +01:00
BLOCKTYPE ClickedBlock ;
NIBBLETYPE ClickedBlockMeta ;
NIBBLETYPE EquippedBlockDamage = ( NIBBLETYPE )( m_Player -> GetEquippedItem (). m_ItemDamage );
2013-08-25 16:11:19 +02: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 19:38:39 +01:00
World -> GetBlockTypeMeta ( a_BlockX , a_BlockY , a_BlockZ , ClickedBlock , ClickedBlockMeta );
2013-08-25 16:11:19 +02:00
// Special slab handling - placing a slab onto another slab produces a dblslab instead:
2013-08-23 19:38:39 +01:00
if (
2013-08-25 16:11:19 +02:00
cBlockSlabHandler :: IsAnySlabType ( ClickedBlock ) && // Is there a slab already?
cBlockSlabHandler :: IsAnySlabType ( EquippedBlock ) && // Is the player placing another slab?
2014-02-28 22:32:10 +01:00
(( ClickedBlockMeta & 0x07 ) == EquippedBlockDamage ) && // Is it the same slab type?
2013-08-25 16:11:19 +02: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 19:38:39 +01:00
)
2013-08-25 16:11:19 +02: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 20:34:11 +01:00
if (( ClickedBlockMeta & 0x08 ) && ( a_BlockFace == BLOCK_FACE_TOP ))
2013-12-02 09:32:28 -07: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 18:46:19 +01: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 06:01:13 -08: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 19:38:39 +01:00
AddFaceDirection ( a_BlockX , a_BlockY , a_BlockZ , a_BlockFace );
2013-08-24 18:46:19 +01:00
2013-08-25 16:11:19 +02: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 16:11:19 +02:00
2013-08-24 18:46:19 +01: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 16:11:19 +02:00
if ( cBlockSlabHandler :: IsAnySlabType ( PlaceBlock ))
2013-08-23 19:38:39 +01:00
{
2013-08-25 16:11:19 +02:00
// It's a slab, don't do checks and proceed to double-slabbing
2013-08-23 19:38:39 +01:00
}
else
{
2013-11-30 00:31:21 +00:00
if (
! BlockHandler ( PlaceBlock ) -> DoesIgnoreBuildCollision () &&
! BlockHandler ( PlaceBlock ) -> DoesIgnoreBuildCollision ( m_Player , PlaceMeta )
)
2013-08-23 19:38:39 +01: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 19:38:39 +01:00
return ;
}
}
2012-06-14 13:06:06 +00:00
}
}
2013-08-24 18:46:19 +01: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:
World -> SendBlockTo ( a_BlockX , a_BlockY , a_BlockY , m_Player );
2014-05-09 23:10:02 +02: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 23:10:02 +02: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 15:17:41 -08:00
cChunkInterface ChunkInterface ( World -> GetChunkMap ());
2014-02-01 05:06:32 -08: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
2013-08-25 16:11:19 +02:00
World -> BroadcastSoundEffect ( NewBlock -> GetStepSound (), a_BlockX * 8 , a_BlockY * 8 , a_BlockZ * 8 , 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 20:08:05 +02: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 20:08:05 +02:00
// If a command, perform it:
AString Message ( a_Message );
if ( cRoot :: Get () -> GetServer () -> Command ( * this , Message ))
{
return ;
}
2014-02-16 23:51:32 +01:00
// Not a command, broadcast as a message:
cCompositeChat Msg ;
AString Color = m_Player -> GetColor ();
if ( Color . length () == 3 )
{
Color = AString ( "@" ) + Color [ 2 ];
}
else
2014-04-18 21:09:44 +02:00
{
Color . clear ();
2014-02-16 23:51:32 +01:00
}
Msg . AddTextPart ( AString ( "<" ) + m_Player -> GetName () + "> " , Color );
Msg . ParseText ( a_Message );
Msg . UnderlineUrls ();
2013-08-14 20:08:05 +02: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 18:36:06 +02: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 16:38:14 +02: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 14:13:40 -07:00
HandlePlayerLook ( a_Rotation , a_Pitch , a_IsOnGround );
2014-05-27 00:28:46 -07: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 12:12:20 +02: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 )
{
case 0 : // No animation - wiki.vg doesn't say that client has something specific for it, so I suppose it will just become -1
case 1 :
case 2 :
case 3 :
{
a_Animation -- ; // Offset by -1
break ;
}
case 5 :
case 6 :
case 7 :
{
a_Animation -= 2 ; // Offset by -2
break ;
}
default : // Anything else is the same
break ;
}
2013-12-06 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-06 00:04:49 +02: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 (
int a_BlockX , int a_BlockY , int a_BlockZ ,
const AString & a_Line1 , const AString & a_Line2 ,
const AString & a_Line3 , const AString & a_Line4
)
2012-06-14 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 08:44:06 +02: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 18:36:06 +02: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 11:18:02 +02: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 19:18:06 +02: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 17:55:53 +01:00
void cClientHandle :: HandleEntityCrouch ( int a_EntityID , bool a_IsCrouching )
2012-09-29 20:43:42 +00:00
{
2013-08-11 19:18:06 +02: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 17:55:53 +01: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 17:55:53 +01: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 17:55:53 +01: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 22:48:59 +02:00
void cClientHandle :: HandleTabCompletion ( const AString & a_Text )
{
AStringVector Results ;
m_Player -> GetWorld () -> TabCompleteUserName ( a_Text , Results );
2013-07-31 11:16:11 +02:00
cRoot :: Get () -> GetPluginManager () -> TabCompleteCommand ( a_Text , Results , m_Player );
2013-07-30 22:48:59 +02:00
if ( Results . empty ())
{
return ;
}
std :: sort ( Results . begin (), Results . end ());
SendTabCompletionResults ( Results );
}
2014-04-04 10:13:25 +02:00
void cClientHandle :: SendData ( const char * a_Data , size_t a_Size )
2012-08-27 17:31:16 +00:00
{
2013-08-22 08:17:26 +02: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 10:13:25 +02: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 22:58:58 +02: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 );
}
2013-08-14 10:24:34 +02:00
void cClientHandle :: MoveToWorld ( cWorld & a_World , bool a_SendRespawnPacket )
{
2013-12-22 14:05:53 +00:00
UNUSED ( a_World );
2013-08-14 10:24:34 +02:00
ASSERT ( m_Player != NULL );
if ( a_SendRespawnPacket )
{
SendRespawn ();
}
cWorld * World = m_Player -> GetWorld ();
// 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 )
{
World -> RemoveChunkClient ( itr -> m_ChunkX , itr -> m_ChunkZ , this );
m_Protocol -> SendUnloadChunk ( itr -> m_ChunkX , itr -> m_ChunkZ );
} // for itr - Chunks[]
2014-03-29 01:21:56 +00:00
// StreamChunks() called in cPlayer::MoveToWorld() after new world has been set
// Meanwhile here, we set last streamed values to bogus ones so everything is resent
2013-08-14 10:24:34 +02:00
m_LastStreamedChunkX = 0x7fffffff ;
m_LastStreamedChunkZ = 0x7fffffff ;
2013-09-08 18:36:06 +02:00
m_HasSentPlayerChunk = false ;
2013-08-14 10:24:34 +02: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-20 22:27:29 +02:00
// Handle clients that are waiting for final close while destroyed:
if ( m_State == csDestroyedWaiting )
{
m_TicksSinceDestruction += 1 ; // This field is misused for the timeout counting
if ( m_TicksSinceDestruction > TICKS_BEFORE_CLOSE )
{
m_State = csDestroyed ;
}
return ;
}
2013-08-13 22:45:29 +02: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 08:35:13 +02:00
if ( m_Player == NULL )
{
return ;
}
2013-09-08 18:36:06 +02:00
// If the chunk the player's in was just sent, spawn the player:
2013-12-15 20:12:40 +01:00
if ( m_HasSentPlayerChunk && ( m_State == csDownloadingWorld ))
2013-09-08 18:36:06 +02:00
{
m_Protocol -> SendPlayerMoveLook ();
m_State = csPlaying ;
}
2013-07-03 07:47:35 +00:00
// Send a ping packet:
2014-04-15 23:40:06 +02:00
if ( m_State == csPlaying )
2012-06-14 13:06:06 +00:00
{
2014-04-15 23:40:06 +02: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 08:35:13 +02: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 10:41:35 +01:00
void cClientHandle :: ServerTick ( float a_Dt )
{
// Handle clients that are waiting for final close while destroyed:
if ( m_State == csDestroyedWaiting )
{
// Do not wait while the client is not in the world, simply cut them off.
m_State = csDestroyed ;
return ;
}
// Process received network data:
AString IncomingData ;
{
cCSLock Lock ( m_CSIncomingData );
std :: swap ( IncomingData , m_IncomingData );
}
m_Protocol -> DataReceived ( IncomingData . data (), IncomingData . size ());
if ( m_State == csAuthenticated )
{
StreamChunks ();
// Remove the client handle from the server, it will be ticked from its cPlayer object from now on
cRoot :: Get () -> GetServer () -> ClientMovedToWorld ( this );
// Add the player to the world (start ticking from there):
m_State = csDownloadingWorld ;
m_Player -> GetWorld () -> AddPlayer ( m_Player );
return ;
}
m_TimeSinceLastPacket += a_Dt ;
if ( m_TimeSinceLastPacket > 30000.f ) // 30 seconds time-out
{
SendDisconnect ( "Nooooo!! You timed out! D: Come back!" );
Destroy ();
}
}
2013-07-07 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 21:55:03 +02: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 23:16:44 +01: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-24 19:14:00 -07: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-24 17:24:39 -07:00
AString Message = FormatMessageType ( ShouldAppendChatPrefixes , a_ChatPrefix , a_AdditionalData );
2014-02-07 18:58:52 +00:00
2014-04-24 17:24:39 -07:00
m_Protocol -> SendChat ( Message . append ( a_Message ));
2012-08-19 11:51:17 +00:00
}
2014-02-15 23:16:44 +01: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 21:13:28 +02: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 18:36:06 +02: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
}
2013-07-07 13:06:06 +00:00
void cClientHandle :: SendCollectPickup ( const cPickup & a_Pickup , const cPlayer & a_Player )
2012-08-19 11:51:17 +00:00
{
2013-07-07 13:06:06 +00:00
m_Protocol -> SendCollectPickup ( a_Pickup , 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 21:42:05 +02: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 18:19:56 +01: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 13:16:48 +01: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 17:13:09 +02: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 20:50:08 +02:00
void cClientHandle :: SendMapDecorators ( int a_ID , const cMapDecoratorList & a_Decorators )
{
m_Protocol -> SendMapDecorators ( a_ID , a_Decorators );
}
2014-02-13 17:13:09 +02:00
void cClientHandle :: SendMapInfo ( int a_ID , unsigned int a_Scale )
{
m_Protocol -> SendMapInfo ( a_ID , a_Scale );
}
2013-12-22 14:45:25 +01: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 14:48:17 +01: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 ;
}
2012-11-16 15:56:41 +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 11:39:42 +01:00
void cClientHandle :: SendPluginMessage ( const AString & a_Channel , const AString & a_Message )
{
m_Protocol -> SendPluginMessage ( a_Channel , a_Message );
}
2013-12-14 18:19:56 +01:00
void cClientHandle :: SendRemoveEntityEffect ( const cEntity & a_Entity , int a_EffectID )
{
m_Protocol -> SendRemoveEntityEffect ( a_Entity , a_EffectID );
}
2013-07-07 13:06:06 +00:00
void cClientHandle :: SendRespawn ( void )
2012-08-24 07:58:26 +00:00
{
2013-07-07 13:06:06 +00:00
m_Protocol -> SendRespawn ();
2012-08-24 07:58:26 +00:00
}
2013-11-16 02:23:50 +11:00
void cClientHandle :: SendExperience ( void )
2013-11-15 22:42:09 +11:00
{
2013-11-16 02:23:50 +11:00
m_Protocol -> SendExperience ();
2013-11-15 22:42:09 +11:00
}
2013-11-25 21:43:43 +01:00
void cClientHandle :: SendExperienceOrb ( const cExpOrb & a_ExpOrb )
2013-11-25 20:04:39 +01:00
{
2013-11-25 21:43:43 +01:00
m_Protocol -> SendExperienceOrb ( a_ExpOrb );
2013-11-25 20:04:39 +01:00
}
2014-01-21 15:58:17 +02:00
void cClientHandle :: SendScoreboardObjective ( const AString & a_Name , const AString & a_DisplayName , Byte a_Mode )
{
m_Protocol -> SendScoreboardObjective ( a_Name , a_DisplayName , a_Mode );
}
void cClientHandle :: SendScoreUpdate ( const AString & a_Objective , const AString & a_Player , cObjective :: Score a_Score , Byte a_Mode )
{
m_Protocol -> SendScoreUpdate ( a_Objective , a_Player , a_Score , a_Mode );
}
void cClientHandle :: SendDisplayObjective ( const AString & a_Objective , cScoreboard :: eDisplaySlot a_Display )
{
m_Protocol -> SendDisplayObjective ( a_Objective , a_Display );
}
2013-07-07 13:06:06 +00:00
void cClientHandle :: SendSoundEffect ( const AString & a_SoundName , int a_SrcX , int a_SrcY , int a_SrcZ , float a_Volume , float a_Pitch )
2012-12-26 09:12:00 +00:00
{
2013-07-07 13:06:06 +00:00
m_Protocol -> SendSoundEffect ( a_SoundName , a_SrcX , a_SrcY , a_SrcZ , 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
}
2013-09-22 19:35:35 +01: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 13:47:22 +01:00
m_Protocol -> SendSpawnVehicle ( a_Vehicle , a_VehicleType , a_VehicleSubType );
2012-08-25 17:52:08 +00:00
}
2014-05-11 14:57:06 +03:00
void cClientHandle :: SendStatistics ( const cStatManager & a_Manager )
{
m_Protocol -> SendStatistics ( a_Manager );
}
2013-07-30 22:48:59 +02: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 13:26:37 +02:00
void cClientHandle :: SendWholeInventory ( const cWindow & a_Window )
2012-09-30 16:37:44 +00:00
{
2013-08-18 13:26:37 +02: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 13:26:37 +02:00
void cClientHandle :: SendWindowClose ( const cWindow & a_Window )
2013-02-18 16:48:50 +00:00
{
2013-08-18 13:26:37 +02:00
m_Protocol -> SendWindowClose ( a_Window );
2013-02-18 16:48:50 +00:00
}
2013-11-08 21:32:14 +01:00
void cClientHandle :: SendWindowOpen ( const cWindow & a_Window )
2012-08-26 21:01:07 +00:00
{
2013-11-08 21:32:14 +01:00
m_Protocol -> SendWindowOpen ( a_Window );
2013-08-18 13:26:37 +02: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 23:24:39 +01: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 23:05:23 +01:00
void cClientHandle :: PacketUnknown ( UInt32 a_PacketType )
2012-08-27 17:31:16 +00:00
{
2013-12-12 23:05:23 +01: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 23:05:23 +01: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 15:08:15 +02:00
bool cClientHandle :: DataReceived ( const char * a_Data , size_t a_Size )
2012-08-27 17:31:16 +00:00
{
2013-08-13 22:45:29 +02: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 22:45:29 +02:00
cCSLock Lock ( m_CSIncomingData );
m_IncomingData . append ( a_Data , a_Size );
2014-05-01 15:08:15 +02: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 11:18:02 +02:00
if ( ! m_Username . empty ()) // Ignore client pings
2013-12-18 18:17:17 +00:00
{
2014-05-02 11:18:02 +02: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 14:33:03 +02: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 19:31:43 +02:00
if ( Item . EnchantByXPLevels ( BaseEnchantmentLevel ))
2014-04-16 14:33:03 +02:00
{
2014-04-18 14:10:31 +02:00
if ( m_Player -> IsGameModeCreative () || m_Player -> DeltaExperience ( - m_Player -> XpForLevel ( BaseEnchantmentLevel )) >= 0 )
2014-04-17 19:31:43 +02:00
{
Window -> m_SlotArea -> SetSlot ( 0 , * m_Player , Item );
Window -> SendSlot ( * m_Player , Window -> m_SlotArea , 0 );
Window -> BroadcastWholeWindow ();
2014-04-16 14:33:03 +02:00
2014-04-17 19:31:43 +02:00
Window -> SetProperty ( 0 , 0 , * m_Player );
Window -> SetProperty ( 1 , 0 , * m_Player );
Window -> SetProperty ( 2 , 0 , * m_Player );
}
2014-04-16 14:33:03 +02:00
}
}