2012-01-29 11:28:19 -08:00
# include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
2011-10-03 11:41:19 -07:00
# include "cClientHandle.h"
# include "cServer.h"
# include "cWorld.h"
# include "cPickup.h"
# include "cPluginManager.h"
# include "cPlayer.h"
# include "cInventory.h"
# include "cChestEntity.h"
# include "cSignEntity.h"
# include "cWindow.h"
# include "cCraftingWindow.h"
# include "cItem.h"
# include "cTorch.h"
# include "cStairs.h"
2011-11-09 18:05:51 -08:00
# include "cDoors.h"
2011-10-03 11:41:19 -07:00
# include "cLadder.h"
# include "cSign.h"
2011-11-03 22:01:55 -07:00
# include "cRedstone.h"
2011-11-05 11:28:19 -07:00
# include "cPiston.h"
2011-10-03 11:41:19 -07:00
# include "cBlockToPickup.h"
# include "cMonster.h"
# include "cChatColor.h"
# include "cSocket.h"
2011-12-27 10:39:06 -08:00
# include "cTimer.h"
2011-10-03 11:41:19 -07:00
# include "cTracer.h"
# include "Vector3f.h"
# include "Vector3d.h"
# include "cSleep.h"
# include "cRoot.h"
# include "cBlockingTCPLink.h"
# include "cAuthenticator.h"
2011-12-27 10:39:06 -08:00
# include "MersenneTwister.h"
2011-10-03 11:41:19 -07:00
# include "packets/cPacket_KeepAlive.h"
# include "packets/cPacket_Respawn.h"
# include "packets/cPacket_UpdateHealth.h"
# include "packets/cPacket_RelativeEntityMoveLook.h"
# include "packets/cPacket_Chat.h"
# include "packets/cPacket_Login.h"
# include "packets/cPacket_WindowClick.h"
# include "packets/cPacket_TimeUpdate.h"
# include "packets/cPacket_BlockDig.h"
# include "packets/cPacket_Handshake.h"
# include "packets/cPacket_ArmAnim.h"
# include "packets/cPacket_BlockPlace.h"
# include "packets/cPacket_Flying.h"
# include "packets/cPacket_Disconnect.h"
# include "packets/cPacket_PickupSpawn.h"
# include "packets/cPacket_ItemSwitch.h"
# include "packets/cPacket_EntityEquipment.h"
2011-12-31 20:55:17 -08:00
# include "packets/cPacket_CreativeInventoryAction.h"
2011-11-02 13:40:39 -07:00
# include "packets/cPacket_NewInvalidState.h"
2011-10-03 11:41:19 -07:00
# include "packets/cPacket_UseEntity.h"
# include "packets/cPacket_WindowClose.h"
# include "packets/cPacket_13.h"
# include "packets/cPacket_UpdateSign.h"
# include "packets/cPacket_Ping.h"
2012-02-13 13:47:03 -08:00
# include "packets/cPacket_NamedEntitySpawn.h"
2012-02-20 08:39:00 -08:00
# include "packets/cPacket_MapChunk.h"
2012-02-22 11:31:30 -08:00
# include "packets/cPacket_PreChunk.h"
2012-02-13 13:47:03 -08:00
2011-10-03 11:41:19 -07:00
2012-02-03 06:33:40 -08:00
# define AddPistonDir(x, y, z, dir, amount) switch (dir) { case 0: (y)-=(amount); break; case 1: (y)+=(amount); break;\
2011-11-07 14:59:29 -08:00
case 2 : ( z ) - = ( amount ) ; break ; case 3 : ( z ) + = ( amount ) ; break ; \
case 4 : ( x ) - = ( amount ) ; break ; case 5 : ( x ) + = ( amount ) ; break ; }
2012-02-25 16:36:51 -08:00
/// If the number of queued outgoing packets reaches this, the client will be kicked
# define MAX_OUTGOING_PACKETS 2000
2011-10-03 11:41:19 -07:00
2012-01-31 10:06:24 -08:00
2012-03-09 05:42:28 -08:00
int cClientHandle : : s_ClientCount = 0 ;
2012-01-31 10:06:24 -08:00
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cClientHandle:
2011-10-03 11:41:19 -07:00
2012-02-23 14:51:03 -08:00
cClientHandle : : cClientHandle ( const cSocket & a_Socket , int a_ViewDistance )
: m_ViewDistance ( a_ViewDistance )
2012-03-26 11:09:10 -07:00
, m_ProtocolVersion ( MCS_PROTOCOL_VERSION )
2012-02-03 06:33:40 -08:00
, m_Socket ( a_Socket )
, m_bDestroyed ( false )
2012-02-02 13:13:24 -08:00
, m_Player ( NULL )
2012-02-03 06:33:40 -08:00
, m_bKicking ( false )
, m_TimeLastPacket ( cWorld : : GetTime ( ) )
, m_bKeepThreadGoing ( true )
2011-12-25 19:05:31 -08:00
, m_Ping ( 1000 )
2012-02-13 13:47:03 -08:00
, m_State ( csConnected )
, m_LastStreamedChunkX ( 0x7fffffff ) // bogus chunk coords to force streaming upon login
, m_LastStreamedChunkZ ( 0x7fffffff )
2012-03-09 05:42:28 -08:00
, m_UniqueID ( 0 )
2011-10-03 11:41:19 -07:00
{
2012-03-09 05:42:28 -08:00
s_ClientCount + + ; // Not protected by CS because clients are always constructed from the same thread
m_UniqueID = s_ClientCount ;
2011-12-27 10:39:06 -08:00
cTimer t1 ;
m_LastPingTime = t1 . GetNowTime ( ) ;
2011-10-03 11:41:19 -07:00
// All the packets that can be received from the client
2012-02-03 06:33:40 -08:00
for ( int i = 0 ; i < ARRAYCOUNT ( m_PacketMap ) ; + + i )
{
m_PacketMap [ i ] = NULL ;
}
m_PacketMap [ E_KEEP_ALIVE ] = new cPacket_KeepAlive ;
m_PacketMap [ E_HANDSHAKE ] = new cPacket_Handshake ;
m_PacketMap [ E_LOGIN ] = new cPacket_Login ;
m_PacketMap [ E_PLAYERPOS ] = new cPacket_PlayerPosition ;
m_PacketMap [ E_PLAYERLOOK ] = new cPacket_PlayerLook ;
m_PacketMap [ E_PLAYERMOVELOOK ] = new cPacket_PlayerMoveLook ;
2012-03-24 06:28:53 -07:00
m_PacketMap [ E_PLAYER_ABILITIES ] = new cPacket_PlayerAbilities ;
2012-02-03 06:33:40 -08:00
m_PacketMap [ E_CHAT ] = new cPacket_Chat ;
m_PacketMap [ E_ANIMATION ] = new cPacket_ArmAnim ;
m_PacketMap [ E_FLYING ] = new cPacket_Flying ;
m_PacketMap [ E_BLOCK_DIG ] = new cPacket_BlockDig ;
m_PacketMap [ E_BLOCK_PLACE ] = new cPacket_BlockPlace ;
m_PacketMap [ E_DISCONNECT ] = new cPacket_Disconnect ;
m_PacketMap [ E_ITEM_SWITCH ] = new cPacket_ItemSwitch ;
m_PacketMap [ E_ENTITY_EQUIPMENT ] = new cPacket_EntityEquipment ;
m_PacketMap [ E_CREATIVE_INVENTORY_ACTION ] = new cPacket_CreativeInventoryAction ;
m_PacketMap [ E_NEW_INVALID_STATE ] = new cPacket_NewInvalidState ;
m_PacketMap [ E_PICKUP_SPAWN ] = new cPacket_PickupSpawn ;
m_PacketMap [ E_USE_ENTITY ] = new cPacket_UseEntity ;
m_PacketMap [ E_WINDOW_CLOSE ] = new cPacket_WindowClose ;
m_PacketMap [ E_WINDOW_CLICK ] = new cPacket_WindowClick ;
m_PacketMap [ E_PACKET_13 ] = new cPacket_13 ;
m_PacketMap [ E_UPDATE_SIGN ] = new cPacket_UpdateSign ;
m_PacketMap [ E_RESPAWN ] = new cPacket_Respawn ;
m_PacketMap [ E_PING ] = new cPacket_Ping ;
2012-02-02 13:13:24 -08:00
2012-02-08 02:02:46 -08:00
LOG ( " New ClientHandle created at %p " , this ) ;
2011-10-03 11:41:19 -07:00
}
2012-01-31 10:06:24 -08:00
2011-10-03 11:41:19 -07:00
cClientHandle : : ~ cClientHandle ( )
{
2012-02-25 16:36:51 -08:00
LOG ( " Deleting client \" %s \" at %p " , GetUsername ( ) . c_str ( ) , this ) ;
2011-10-03 11:41:19 -07:00
2012-02-25 15:48:28 -08:00
// Remove from cSocketThreads, we're not to be called anymore:
2012-02-13 13:47:03 -08:00
cRoot : : Get ( ) - > GetServer ( ) - > ClientDestroying ( this ) ;
2012-02-28 06:58:07 -08:00
{
cCSLock Lock ( m_CSChunkLists ) ;
m_LoadedChunks . clear ( ) ;
m_ChunksToSend . clear ( ) ;
}
2011-10-03 11:41:19 -07:00
2012-02-13 13:47:03 -08:00
if ( m_Player ! = NULL )
2011-12-25 18:46:53 -08:00
{
2012-02-13 13:47:03 -08:00
cWorld * World = m_Player - > GetWorld ( ) ;
if ( ! m_Username . empty ( ) & & ( World ! = NULL ) )
2011-12-25 18:46:53 -08:00
{
2012-02-13 13:47:03 -08:00
// Send the Offline PlayerList packet:
AString NameColor = ( m_Player ? m_Player - > GetColor ( ) : " " ) ;
2012-01-02 11:03:19 -08:00
cPacket_PlayerListItem PlayerList ( NameColor + GetUsername ( ) , false , ( short ) 9999 ) ;
2012-02-13 13:47:03 -08:00
World - > Broadcast ( PlayerList , this ) ;
// Send the Chat packet:
cPacket_Chat Left ( m_Username + " left the game! " ) ;
World - > Broadcast ( Left , this ) ;
2011-12-25 18:46:53 -08:00
}
2012-02-26 04:55:42 -08:00
if ( World ! = NULL )
{
World - > RemovePlayer ( m_Player ) ;
}
2011-12-25 19:05:31 -08:00
}
2011-12-25 18:46:53 -08:00
2012-02-03 06:33:40 -08:00
if ( m_Socket . IsValid ( ) )
2011-10-03 11:41:19 -07:00
{
cPacket_Disconnect Disconnect ;
Disconnect . m_Reason = " Server shut down? Kthnxbai " ;
2012-02-07 12:49:52 -08:00
m_Socket . Send ( & Disconnect ) ;
2011-10-03 11:41:19 -07:00
}
2012-02-25 16:36:51 -08:00
2012-02-02 13:13:24 -08:00
if ( m_Player ! = NULL )
2011-10-03 11:41:19 -07:00
{
2012-01-01 08:20:52 -08:00
m_Player - > Destroy ( ) ;
2012-02-02 13:13:24 -08:00
m_Player = NULL ;
2012-01-01 08:20:52 -08:00
}
2012-02-03 06:33:40 -08:00
for ( int i = 0 ; i < ARRAYCOUNT ( m_PacketMap ) ; i + + )
2011-10-03 11:41:19 -07:00
{
2012-02-03 06:33:40 -08:00
delete m_PacketMap [ i ] ;
2011-10-03 11:41:19 -07:00
}
2012-02-13 13:47:03 -08:00
2012-02-25 16:36:51 -08:00
// Queue all remaining outgoing packets to cSocketThreads:
2012-02-13 13:47:03 -08:00
{
2012-02-25 16:36:51 -08:00
cCSLock Lock ( m_CSPackets ) ;
2012-02-13 13:47:03 -08:00
for ( PacketList : : iterator itr = m_PendingNrmSendPackets . begin ( ) ; itr ! = m_PendingNrmSendPackets . end ( ) ; + + itr )
{
2012-02-25 16:36:51 -08:00
AString Data ;
( * itr ) - > Serialize ( Data ) ;
cRoot : : Get ( ) - > GetServer ( ) - > WriteToClient ( & m_Socket , Data ) ;
2012-02-13 13:47:03 -08:00
delete * itr ;
}
2012-02-25 16:36:51 -08:00
m_PendingNrmSendPackets . clear ( ) ;
2012-02-13 13:47:03 -08:00
for ( PacketList : : iterator itr = m_PendingLowSendPackets . begin ( ) ; itr ! = m_PendingLowSendPackets . end ( ) ; + + itr )
{
2012-02-25 16:36:51 -08:00
AString Data ;
( * itr ) - > Serialize ( Data ) ;
cRoot : : Get ( ) - > GetServer ( ) - > WriteToClient ( & m_Socket , Data ) ;
2012-02-13 13:47:03 -08:00
delete * itr ;
}
2012-02-25 16:36:51 -08:00
m_PendingLowSendPackets . clear ( ) ;
2012-02-13 13:47:03 -08:00
}
2012-02-08 02:02:46 -08:00
2012-02-25 16:36:51 -08:00
// Queue the socket to close as soon as it sends all outgoing data:
cRoot : : Get ( ) - > GetServer ( ) - > QueueClientClose ( & m_Socket ) ;
2012-02-26 04:55:42 -08:00
// We need to remove the socket from SocketThreads because we own it and it gets destroyed after this destructor finishes
// TODO: The socket needs to stay alive, someone else has to own it
cRoot : : Get ( ) - > GetServer ( ) - > RemoveClient ( & m_Socket ) ;
2012-02-25 16:36:51 -08:00
LOG ( " ClientHandle at %p deleted " , this ) ;
2011-10-03 11:41:19 -07:00
}
2012-01-31 10:06:24 -08:00
2011-10-03 11:41:19 -07:00
void cClientHandle : : Destroy ( )
{
2012-02-28 06:58:07 -08:00
// Setting m_bDestroyed was moved to the bottom of Destroy(),
// otherwise the destructor may be called within another thread before the client is removed from chunks
// http://forum.mc-server.org/showthread.php?tid=366
2012-02-13 13:47:03 -08:00
if ( ( m_Player ! = NULL ) & & ( m_Player - > GetWorld ( ) ! = NULL ) )
{
RemoveFromAllChunks ( ) ;
2012-03-06 06:52:44 -08:00
m_Player - > GetWorld ( ) - > RemoveClientFromChunkSender ( this ) ;
2012-02-13 13:47:03 -08:00
}
2012-02-28 06:58:07 -08:00
m_bDestroyed = true ;
2011-10-03 11:41:19 -07:00
}
2012-01-31 10:06:24 -08:00
2012-02-01 14:38:03 -08:00
void cClientHandle : : Kick ( const AString & a_Reason )
2011-10-03 11:41:19 -07:00
{
2012-02-13 13:47:03 -08:00
if ( m_State > = csAuthenticating ) // Don't log pings
{
LOG ( " Kicking user \" %s \" for \" %s \" " , m_Username . c_str ( ) , a_Reason . c_str ( ) ) ;
}
2012-02-01 14:38:03 -08:00
Send ( cPacket_Disconnect ( a_Reason ) ) ;
2011-10-03 11:41:19 -07:00
m_bKicking = true ;
}
2012-01-31 10:06:24 -08:00
2012-02-01 14:38:03 -08:00
void cClientHandle : : Authenticate ( void )
{
2012-02-29 12:25:11 -08:00
if ( m_State ! = csAuthenticating )
{
return ;
}
2012-02-29 05:40:40 -08:00
ASSERT ( m_Player = = NULL ) ;
2012-02-29 12:25:11 -08:00
2012-02-13 13:47:03 -08: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 ( ) ;
}
m_Player - > LoginSetGameMode ( World - > GetGameMode ( ) ) ; //set player's gamemode to server's gamemode at login. TODO: set to last player's gamemode at logout
m_Player - > SetIP ( m_Socket . GetIPString ( ) ) ;
cRoot : : Get ( ) - > GetPluginManager ( ) - > CallHook ( cPluginManager : : E_PLUGIN_PLAYER_SPAWN , 1 , m_Player ) ;
// Return a server login packet
cPacket_Login LoginResponse ;
LoginResponse . m_ProtocolVersion = m_Player - > GetUniqueID ( ) ;
//LoginResponse.m_Username = "";
LoginResponse . m_ServerMode = m_Player - > GetGameMode ( ) ; // set gamemode from player.
LoginResponse . m_Dimension = 0 ;
2012-03-10 14:27:24 -08:00
LoginResponse . m_MaxPlayers = ( unsigned char ) cRoot : : Get ( ) - > GetDefaultWorld ( ) - > GetMaxPlayers ( ) ;
2012-02-13 13:47:03 -08:00
LoginResponse . m_Difficulty = 2 ;
Send ( LoginResponse ) ;
// Send Weather if raining:
if ( ( World - > GetWeather ( ) = = 1 ) | | ( World - > GetWeather ( ) = = 2 ) )
{
cPacket_NewInvalidState RainPacket ;
RainPacket . m_Reason = 1 ; //begin rain
Send ( RainPacket ) ;
}
// Send time
Send ( cPacket_TimeUpdate ( World - > GetWorldTime ( ) ) ) ;
// Send inventory
m_Player - > GetInventory ( ) . SendWholeInventory ( this ) ;
// Send health
cPacket_UpdateHealth Health ;
Health . m_Health = ( short ) m_Player - > GetHealth ( ) ;
Health . m_Food = m_Player - > GetFood ( ) ;
Health . m_Saturation = m_Player - > GetFoodSaturation ( ) ;
Send ( Health ) ;
m_Player - > Initialize ( World ) ;
StreamChunks ( ) ;
2012-02-25 16:36:51 -08:00
m_State = csDownloadingWorld ;
2012-02-01 14:38:03 -08:00
}
void cClientHandle : : StreamChunks ( void )
2011-10-03 11:41:19 -07:00
{
2012-02-25 16:36:51 -08:00
if ( m_State < csAuthenticating )
2012-02-02 13:13:24 -08:00
{
2011-10-03 11:41:19 -07:00
return ;
2012-02-02 13:13:24 -08:00
}
2011-11-01 14:57:08 -07:00
2012-02-19 15:00:00 -08:00
ASSERT ( m_Player ! = NULL ) ;
2012-02-02 13:13:24 -08:00
2012-03-14 13:56:09 -07:00
int ChunkPosX = FAST_FLOOR_DIV ( m_Player - > GetPosX ( ) , cChunkDef : : Width ) ;
int ChunkPosZ = FAST_FLOOR_DIV ( m_Player - > GetPosZ ( ) , cChunkDef : : Width ) ;
2012-02-13 13:47:03 -08:00
if ( ( ChunkPosX = = m_LastStreamedChunkX ) & & ( ChunkPosZ = = m_LastStreamedChunkZ ) )
{
// Already streamed for this position
return ;
}
m_LastStreamedChunkX = ChunkPosX ;
m_LastStreamedChunkZ = ChunkPosZ ;
2012-03-24 06:28:53 -07:00
LOGD ( " Streaming chunks centered on [%d, %d], view distance %d " , ChunkPosX , ChunkPosZ , m_ViewDistance ) ;
2012-02-13 13:47:03 -08:00
2012-02-02 13:13:24 -08:00
cWorld * World = m_Player - > GetWorld ( ) ;
2012-02-19 15:00:00 -08:00
ASSERT ( World ! = NULL ) ;
2011-12-24 15:34:30 -08:00
2012-03-06 06:52:44 -08:00
// Remove all loaded chunks that are no longer in range; deferred to out-of-CS:
cChunkCoordsList RemoveChunks ;
2011-10-03 11:41:19 -07:00
{
2012-02-13 13:47:03 -08:00
cCSLock Lock ( m_CSChunkLists ) ;
for ( cChunkCoordsList : : iterator itr = m_LoadedChunks . begin ( ) ; itr ! = m_LoadedChunks . end ( ) ; )
2011-10-03 11:41:19 -07:00
{
2012-02-13 13:47:03 -08:00
int RelX = ( * itr ) . m_ChunkX - ChunkPosX ;
int RelZ = ( * itr ) . m_ChunkZ - ChunkPosZ ;
2012-02-23 14:51:03 -08:00
if ( ( RelX > m_ViewDistance ) | | ( RelX < - m_ViewDistance ) | | ( RelZ > m_ViewDistance ) | | ( RelZ < - m_ViewDistance ) )
2012-02-02 13:13:24 -08:00
{
2012-03-06 06:52:44 -08:00
RemoveChunks . push_back ( * itr ) ;
2012-02-13 13:47:03 -08:00
itr = m_LoadedChunks . erase ( itr ) ;
2012-02-02 13:13:24 -08:00
}
2012-02-13 13:47:03 -08:00
else
2011-10-03 11:41:19 -07:00
{
2012-02-13 13:47:03 -08:00
+ + itr ;
2011-10-03 11:41:19 -07:00
}
2012-02-13 13:47:03 -08:00
} // for itr - m_LoadedChunks[]
for ( cChunkCoordsList : : iterator itr = m_ChunksToSend . begin ( ) ; itr ! = m_ChunksToSend . end ( ) ; )
2011-10-03 11:41:19 -07:00
{
2012-02-13 13:47:03 -08:00
int RelX = ( * itr ) . m_ChunkX - ChunkPosX ;
int RelZ = ( * itr ) . m_ChunkZ - ChunkPosZ ;
2012-02-23 14:51:03 -08:00
if ( ( RelX > m_ViewDistance ) | | ( RelX < - m_ViewDistance ) | | ( RelZ > m_ViewDistance ) | | ( RelZ < - m_ViewDistance ) )
2012-02-13 13:47:03 -08:00
{
itr = m_ChunksToSend . erase ( itr ) ;
}
else
{
+ + itr ;
}
2012-03-06 06:52:44 -08:00
} // for itr - m_ChunksToSend[]
2011-10-03 11:41:19 -07:00
}
2012-03-06 06:52:44 -08:00
for ( cChunkCoordsList : : iterator itr = RemoveChunks . begin ( ) ; itr ! = RemoveChunks . end ( ) ; + + itr )
{
World - > RemoveChunkClient ( itr - > m_ChunkX , itr - > m_ChunkY , itr - > m_ChunkZ , this ) ;
Send ( cPacket_PreChunk ( itr - > m_ChunkX , itr - > m_ChunkZ , false ) ) ;
} // for itr - RemoveChunks[]
2012-02-13 13:47:03 -08:00
// Add all chunks that are in range and not yet in m_LoadedChunks:
// Queue these smartly - from the center out to the edge
2012-02-23 14:51:03 -08:00
for ( int d = 0 ; d < = m_ViewDistance ; + + d ) // cycle through (square) distance, from nearest to furthest
2012-02-02 13:13:24 -08:00
{
2012-02-13 13:47:03 -08:00
// For each distance add chunks in a hollow square centered around current position:
for ( int i = - d ; i < = d ; + + i )
2011-10-03 11:41:19 -07:00
{
2012-02-17 09:56:25 -08:00
StreamChunk ( ChunkPosX + d , ZERO_CHUNK_Y , ChunkPosZ + i ) ;
StreamChunk ( ChunkPosX - d , ZERO_CHUNK_Y , ChunkPosZ + i ) ;
2012-02-13 13:47:03 -08:00
} // for i
for ( int i = - d + 1 ; i < d ; + + i )
{
2012-02-17 09:56:25 -08:00
StreamChunk ( ChunkPosX + i , ZERO_CHUNK_Y , ChunkPosZ + d ) ;
StreamChunk ( ChunkPosX + i , ZERO_CHUNK_Y , ChunkPosZ - d ) ;
2012-02-13 13:47:03 -08:00
} // for i
} // for d
// Touch chunks GENERATEDISTANCE ahead to let them generate:
2012-02-23 14:51:03 -08:00
for ( int d = m_ViewDistance + 1 ; d < = m_ViewDistance + GENERATEDISTANCE ; + + d ) // cycle through (square) distance, from nearest to furthest
2012-02-13 13:47:03 -08:00
{
// For each distance touch chunks in a hollow square centered around current position:
for ( int i = - d ; i < = d ; + + i )
{
2012-02-21 08:27:30 -08:00
World - > TouchChunk ( ChunkPosX + d , ZERO_CHUNK_Y , ChunkPosZ + i ) ;
World - > TouchChunk ( ChunkPosX - d , ZERO_CHUNK_Y , ChunkPosZ + i ) ;
2012-02-13 13:47:03 -08:00
} // for i
for ( int i = - d + 1 ; i < d ; + + i )
{
2012-02-21 08:27:30 -08:00
World - > TouchChunk ( ChunkPosX + i , ZERO_CHUNK_Y , ChunkPosZ + d ) ;
World - > TouchChunk ( ChunkPosX + i , ZERO_CHUNK_Y , ChunkPosZ - d ) ;
2012-02-13 13:47:03 -08:00
} // for i
} // for d
}
2011-12-24 15:34:30 -08:00
2011-10-03 11:41:19 -07:00
2012-02-13 13:47:03 -08:00
2012-02-17 09:56:25 -08:00
void cClientHandle : : StreamChunk ( int a_ChunkX , int a_ChunkY , int a_ChunkZ )
2012-02-13 13:47:03 -08:00
{
cWorld * World = m_Player - > GetWorld ( ) ;
2012-02-19 15:00:00 -08:00
ASSERT ( World ! = NULL ) ;
2012-02-13 13:47:03 -08:00
2012-02-21 07:18:02 -08:00
if ( World - > AddChunkClient ( a_ChunkX , a_ChunkY , a_ChunkZ , this ) )
2012-02-13 13:47:03 -08:00
{
cCSLock Lock ( m_CSChunkLists ) ;
2012-02-17 09:56:25 -08:00
m_LoadedChunks . push_back ( cChunkCoords ( a_ChunkX , a_ChunkY , a_ChunkZ ) ) ;
m_ChunksToSend . push_back ( cChunkCoords ( a_ChunkX , a_ChunkY , a_ChunkZ ) ) ;
2012-03-06 06:52:44 -08:00
World - > SendChunkTo ( a_ChunkX , a_ChunkY , a_ChunkZ , this ) ;
2011-10-03 11:41:19 -07:00
}
}
2012-01-31 10:06:24 -08:00
2012-02-13 13:47:03 -08:00
// Removes the client from all chunks. Used when switching worlds or destroying the player
void cClientHandle : : RemoveFromAllChunks ( )
2011-10-03 11:41:19 -07:00
{
2012-02-13 13:47:03 -08:00
cWorld * World = m_Player - > GetWorld ( ) ;
if ( World ! = NULL )
2011-10-03 11:41:19 -07:00
{
2012-03-22 08:53:40 -07:00
World - > RemoveClientFromChunks ( this ) ;
}
{
cCSLock Lock ( m_CSChunkLists ) ;
m_LoadedChunks . clear ( ) ;
m_ChunksToSend . clear ( ) ;
2011-10-03 11:41:19 -07:00
}
}
2012-01-31 10:06:24 -08:00
2012-02-03 06:33:40 -08:00
void cClientHandle : : HandlePacket ( cPacket * a_Packet )
2011-10-03 11:41:19 -07:00
{
m_TimeLastPacket = cWorld : : GetTime ( ) ;
2012-02-13 13:47:03 -08:00
// LOG("Recv packet 0x%02x from client \"%s\" (\"%s\")", a_Packet->m_PacketID, m_Socket.GetIPString().c_str(), m_Username.c_str());
2011-10-03 11:41:19 -07:00
2012-02-02 13:13:24 -08:00
if ( m_bKicking )
{
return ;
}
2011-10-03 11:41:19 -07:00
2012-02-13 13:47:03 -08:00
switch ( m_State )
2011-10-03 11:41:19 -07:00
{
2012-02-13 13:47:03 -08:00
case csConnected :
2011-10-03 11:41:19 -07:00
{
2012-02-13 13:47:03 -08:00
switch ( a_Packet - > m_PacketID )
2011-12-26 06:29:52 -08:00
{
2012-02-13 13:47:03 -08:00
case E_NEW_INVALID_STATE : // New/Invalid State packet received. I'm guessing the client only sends it when there's a problem with the bed?
{
LOGINFO ( " Got New Invalid State packet " ) ;
break ;
}
case E_PING : HandlePing ( ) ; break ;
case E_HANDSHAKE : HandleHandshake ( reinterpret_cast < cPacket_Handshake * > ( a_Packet ) ) ; break ;
case E_LOGIN : HandleLogin ( reinterpret_cast < cPacket_Login * > ( a_Packet ) ) ; break ;
// Ignored packets:
case E_PLAYERLOOK :
2012-02-25 15:48:28 -08:00
case E_CHAT :
2012-02-13 13:47:03 -08:00
case E_PLAYERMOVELOOK :
case E_PLAYERPOS :
case E_KEEP_ALIVE : break ;
default : HandleUnexpectedPacket ( a_Packet ) ; break ;
} // switch (PacketType)
break ;
} // case csConnected
case csAuthenticating :
{
// Waiting for external authentication, no packets are handled
switch ( a_Packet - > m_PacketID )
{
// Ignored packets:
case E_KEEP_ALIVE :
2012-02-25 15:48:28 -08:00
case E_CHAT :
case E_FLYING :
2012-02-13 13:47:03 -08:00
case E_PLAYERLOOK :
case E_PLAYERMOVELOOK :
case E_PLAYERPOS : break ;
default : HandleUnexpectedPacket ( a_Packet ) ; break ;
2011-12-26 06:29:52 -08:00
}
2012-02-13 13:47:03 -08:00
break ;
}
2011-12-31 20:55:17 -08:00
2012-02-13 13:47:03 -08:00
case csDownloadingWorld :
2012-01-19 10:12:39 -08:00
{
2012-02-13 13:47:03 -08:00
// Waiting for chunks to stream to client, no packets are handled
switch ( a_Packet - > m_PacketID )
{
// Ignored packets:
case E_KEEP_ALIVE :
2012-02-25 15:48:28 -08:00
case E_CHAT :
case E_FLYING :
2012-02-13 13:47:03 -08:00
case E_PLAYERLOOK :
case E_PLAYERMOVELOOK :
case E_PLAYERPOS : break ;
default : HandleUnexpectedPacket ( a_Packet ) ; break ;
}
break ;
}
case csConfirmingPos :
{
switch ( a_Packet - > m_PacketID )
{
// Ignored packets:
case E_KEEP_ALIVE :
2012-02-25 15:48:28 -08:00
case E_CHAT :
case E_FLYING :
2012-02-13 13:47:03 -08:00
case E_PLAYERLOOK :
case E_PLAYERPOS : break ;
2012-01-19 10:12:39 -08:00
2012-02-13 13:47:03 -08:00
case E_PLAYERMOVELOOK : HandleMoveLookConfirm ( reinterpret_cast < cPacket_PlayerMoveLook * > ( a_Packet ) ) ; break ;
default :
{
HandleUnexpectedPacket ( a_Packet ) ;
break ;
}
} // switch (PacketType)
break ;
} // case csConfirmingPos
case csPlaying :
2011-10-03 11:41:19 -07:00
{
2012-02-13 13:47:03 -08:00
switch ( a_Packet - > m_PacketID )
{
case E_CREATIVE_INVENTORY_ACTION : HandleCreativeInventory ( reinterpret_cast < cPacket_CreativeInventoryAction * > ( a_Packet ) ) ; break ;
case E_PLAYERPOS : HandlePlayerPos ( reinterpret_cast < cPacket_PlayerPosition * > ( a_Packet ) ) ; break ;
case E_BLOCK_DIG : HandleBlockDig ( reinterpret_cast < cPacket_BlockDig * > ( a_Packet ) ) ; break ;
case E_BLOCK_PLACE : HandleBlockPlace ( reinterpret_cast < cPacket_BlockPlace * > ( a_Packet ) ) ; break ;
case E_PICKUP_SPAWN : HandlePickupSpawn ( reinterpret_cast < cPacket_PickupSpawn * > ( a_Packet ) ) ; break ;
case E_CHAT : HandleChat ( reinterpret_cast < cPacket_Chat * > ( a_Packet ) ) ; break ;
case E_PLAYERLOOK : HandlePlayerLook ( reinterpret_cast < cPacket_PlayerLook * > ( a_Packet ) ) ; break ;
case E_PLAYERMOVELOOK : HandlePlayerMoveLook ( reinterpret_cast < cPacket_PlayerMoveLook * > ( a_Packet ) ) ; break ;
case E_ANIMATION : HandleAnimation ( reinterpret_cast < cPacket_ArmAnim * > ( a_Packet ) ) ; break ;
case E_ITEM_SWITCH : HandleItemSwitch ( reinterpret_cast < cPacket_ItemSwitch * > ( a_Packet ) ) ; break ;
case E_WINDOW_CLOSE : HandleWindowClose ( reinterpret_cast < cPacket_WindowClose * > ( a_Packet ) ) ; break ;
case E_WINDOW_CLICK : HandleWindowClick ( reinterpret_cast < cPacket_WindowClick * > ( a_Packet ) ) ; break ;
case E_UPDATE_SIGN : HandleUpdateSign ( reinterpret_cast < cPacket_UpdateSign * > ( a_Packet ) ) ; break ;
case E_USE_ENTITY : HandleUseEntity ( reinterpret_cast < cPacket_UseEntity * > ( a_Packet ) ) ; break ;
case E_RESPAWN : HandleRespawn ( ) ; break ;
case E_DISCONNECT : HandleDisconnect ( reinterpret_cast < cPacket_Disconnect * > ( a_Packet ) ) ; break ;
case E_KEEP_ALIVE : HandleKeepAlive ( reinterpret_cast < cPacket_KeepAlive * > ( a_Packet ) ) ; break ;
} // switch (Packet type)
break ;
} // case csPlaying
} // switch (m_State)
2012-02-03 06:33:40 -08:00
}
2012-02-02 13:13:24 -08:00
2011-11-10 08:30:14 -08:00
2011-12-28 13:00:35 -08:00
2011-10-03 11:41:19 -07:00
2012-02-03 06:33:40 -08:00
void cClientHandle : : HandlePing ( void )
{
// Somebody tries to retrieve information about the server
AString Reply ;
Printf ( Reply , " %s%s%i%s%i " ,
2012-03-10 14:27:24 -08:00
cRoot : : Get ( ) - > GetDefaultWorld ( ) - > GetDescription ( ) . c_str ( ) ,
2012-02-03 06:33:40 -08:00
cChatColor : : Delimiter . c_str ( ) ,
2012-03-10 14:27:24 -08:00
cRoot : : Get ( ) - > GetDefaultWorld ( ) - > GetNumPlayers ( ) ,
2012-02-03 06:33:40 -08:00
cChatColor : : Delimiter . c_str ( ) ,
2012-03-10 14:27:24 -08:00
cRoot : : Get ( ) - > GetDefaultWorld ( ) - > GetMaxPlayers ( )
2012-02-03 06:33:40 -08:00
) ;
Kick ( Reply . c_str ( ) ) ;
}
2011-12-24 17:40:31 -08:00
2011-12-28 13:00:35 -08:00
2012-02-03 06:33:40 -08:00
void cClientHandle : : HandleHandshake ( cPacket_Handshake * a_Packet )
{
2012-03-01 09:07:41 -08:00
AStringVector UserData = StringSplit ( a_Packet - > m_Username , " ; " ) ; // "FakeTruth;localhost:25565"
if ( UserData . size ( ) = = 0 )
{
Kick ( " Could not receive username " ) ;
return ;
}
m_Username = UserData [ 0 ] ;
2012-02-03 06:33:40 -08:00
LOG ( " HANDSHAKE %s " , m_Username . c_str ( ) ) ;
2011-11-10 08:30:14 -08:00
2012-03-10 14:27:24 -08:00
if ( cRoot : : Get ( ) - > GetDefaultWorld ( ) - > GetNumPlayers ( ) > = cRoot : : Get ( ) - > GetDefaultWorld ( ) - > GetMaxPlayers ( ) )
2012-02-03 06:33:40 -08:00
{
Kick ( " The server is currently full :(-- Try again later " ) ;
return ;
}
cPacket_Chat Connecting ( m_Username + " is connecting. " ) ;
cRoot : : Get ( ) - > GetServer ( ) - > Broadcast ( Connecting , this ) ;
2011-11-07 14:59:29 -08:00
2012-02-03 06:33:40 -08:00
cPacket_Handshake Handshake ;
Handshake . m_Username = cRoot : : Get ( ) - > GetServer ( ) - > GetServerID ( ) ;
Send ( Handshake ) ;
2012-02-08 04:36:54 -08:00
LOG ( " User \" %s \" was sent a handshake " , m_Username . c_str ( ) ) ;
2012-02-03 06:33:40 -08:00
}
2011-10-03 11:41:19 -07:00
2011-11-10 16:21:52 -08:00
2012-02-03 06:33:40 -08:00
void cClientHandle : : HandleLogin ( cPacket_Login * a_Packet )
{
LOG ( " LOGIN %s " , m_Username . c_str ( ) ) ;
if ( a_Packet - > m_ProtocolVersion < m_ProtocolVersion )
{
Kick ( " Your client is outdated! " ) ;
return ;
}
else if ( a_Packet - > m_ProtocolVersion > m_ProtocolVersion )
{
Kick ( " Your client version is higher than the server! " ) ;
return ;
}
if ( m_Username . compare ( a_Packet - > m_Username ) ! = 0 )
{
LOGWARNING ( " Login Username ( \" %s \" ) does not match Handshake username ( \" %s \" ) for client \" %s \" ) " ,
a_Packet - > m_Username . c_str ( ) ,
m_Username . c_str ( ) ,
m_Socket . GetIPString ( ) . c_str ( )
) ;
Kick ( " Hacked client " ) ; // Don't tell them why we don't want them
return ;
}
2011-10-03 11:41:19 -07:00
2012-02-03 06:33:40 -08:00
if ( cRoot : : Get ( ) - > GetPluginManager ( ) - > CallHook ( cPluginManager : : E_PLUGIN_LOGIN , 1 , a_Packet ) )
{
Destroy ( ) ;
return ;
}
2011-10-03 11:41:19 -07:00
2012-02-03 06:33:40 -08:00
// Schedule for authentication; until then, let them wait (but do not block)
2012-02-13 13:47:03 -08:00
m_State = csAuthenticating ;
2012-03-09 05:42:28 -08:00
cRoot : : Get ( ) - > GetAuthenticator ( ) . Authenticate ( GetUniqueID ( ) , GetUsername ( ) , cRoot : : Get ( ) - > GetServer ( ) - > GetServerID ( ) ) ;
2012-02-03 06:33:40 -08:00
}
2011-10-03 11:41:19 -07:00
2011-12-31 20:55:17 -08:00
2011-11-10 16:21:52 -08:00
2011-11-10 08:30:14 -08:00
2012-02-13 13:47:03 -08:00
void cClientHandle : : HandleUnexpectedPacket ( cPacket * a_Packet )
2012-02-03 06:33:40 -08:00
{
LOGWARNING (
2012-02-13 13:47:03 -08:00
" Invalid packet in state %d: 0x%02x from client \" %s \" , username \" %s \" " ,
m_State ,
2012-02-03 06:33:40 -08:00
a_Packet - > m_PacketID ,
m_Socket . GetIPString ( ) . c_str ( ) ,
m_Username . c_str ( )
) ;
Kick ( " Hacked client " ) ; // Don't tell them why we don't like them
}
2011-12-31 20:55:17 -08:00
2011-11-10 08:30:14 -08:00
2011-12-31 20:55:17 -08:00
2011-12-28 13:00:35 -08:00
2012-02-03 06:33:40 -08:00
void cClientHandle : : HandleMoveLookConfirm ( cPacket_PlayerMoveLook * a_Packet )
{
Vector3d ReceivedPosition = Vector3d ( a_Packet - > m_PosX , a_Packet - > m_PosY , a_Packet - > m_PosZ ) ;
// Test the distance between points with a small/large enough value instead of comparing directly. Floating point inaccuracies might screw stuff up
double Dist = ( ReceivedPosition - m_ConfirmPosition ) . SqrLength ( ) ;
if ( Dist < 1.0 )
{
// Test
if ( ReceivedPosition . Equals ( m_ConfirmPosition ) )
{
LOGINFO ( " Exact position confirmed by client! " ) ;
}
2012-02-13 13:47:03 -08:00
m_State = csPlaying ;
2012-02-03 06:33:40 -08:00
}
else
{
2012-02-13 13:47:03 -08:00
LOGWARNING ( " Player \" %s \" sent a weird position confirmation %.2f blocks away, retrying " , m_Username . c_str ( ) , Dist ) ;
m_ConfirmPosition = m_Player - > GetPosition ( ) ;
Send ( cPacket_PlayerMoveLook ( m_Player ) ) ;
2012-02-03 06:33:40 -08:00
}
}
void cClientHandle : : HandleCreativeInventory ( cPacket_CreativeInventoryAction * a_Packet )
{
// This is for creative Inventory changes
if ( m_Player - > GetGameMode ( ) = = 1 )
{
m_Player - > GetInventory ( ) . Clicked ( a_Packet ) ;
}
else
{
LOGWARNING ( " Got a CreativeInventoryAction packet from user \" %s \" while not in creative mode. Ignoring. " , m_Username . c_str ( ) ) ;
}
}
void cClientHandle : : HandlePlayerPos ( cPacket_PlayerPosition * a_Packet )
{
// LOG("recv player pos: %0.2f %0.2f %0.2f", PacketData->m_PosX, PacketData->m_PosY, PacketData->m_PosZ);
m_Player - > MoveTo ( Vector3d ( a_Packet - > m_PosX , a_Packet - > m_PosY , a_Packet - > m_PosZ ) ) ;
m_Player - > SetStance ( a_Packet - > m_Stance ) ;
m_Player - > SetTouchGround ( a_Packet - > m_bFlying ) ;
}
void cClientHandle : : HandleBlockDig ( cPacket_BlockDig * a_Packet )
{
if ( ! CheckBlockInteractionsRate ( ) )
{
return ;
}
2012-04-03 07:07:18 -07:00
LOGD ( " OnBlockDig: {%i, %i, %i} Dir: %i Stat: %i " ,
a_Packet - > m_PosX , a_Packet - > m_PosY , a_Packet - > m_PosZ ,
a_Packet - > m_Direction , a_Packet - > m_Status
) ;
2012-02-03 06:33:40 -08:00
if ( a_Packet - > m_Status = = 0x04 ) // Drop block
{
m_Player - > TossItem ( false ) ;
return ;
}
cWorld * World = m_Player - > GetWorld ( ) ;
char OldBlock = World - > GetBlock ( a_Packet - > m_PosX , a_Packet - > m_PosY , a_Packet - > m_PosZ ) ;
char MetaData = World - > GetBlockMeta ( a_Packet - > m_PosX , a_Packet - > m_PosY , a_Packet - > m_PosZ ) ;
bool bBroken = (
( a_Packet - > m_Status = = 0x02 ) | |
( g_BlockOneHitDig [ ( int ) OldBlock ] ) | |
( ( a_Packet - > m_Status = = 0x00 ) & & ( m_Player - > GetGameMode ( ) = = 1 ) ) | |
( ( m_Player - > GetInventory ( ) . GetEquippedItem ( ) . m_ItemID = = E_ITEM_SHEARS ) & & ( OldBlock = = E_BLOCK_LEAVES ) )
) ;
if ( ( OldBlock = = E_BLOCK_WOODEN_DOOR ) & & ! bBroken )
{
cDoors : : ChangeDoor ( m_Player - > GetWorld ( ) , a_Packet - > m_PosX , a_Packet - > m_PosY , a_Packet - > m_PosZ ) ;
}
cItem PickupItem ;
if ( bBroken & & ! ( m_Player - > GetGameMode ( ) = = 1 ) ) // broken
{
ENUM_ITEM_ID PickupID = cBlockToPickup : : ToPickup ( ( ENUM_BLOCK_ID ) OldBlock , m_Player - > GetInventory ( ) . GetEquippedItem ( ) . m_ItemID ) ;
PickupItem . m_ItemID = PickupID ;
PickupItem . m_ItemHealth = MetaData ;
PickupItem . m_ItemCount = cBlockToPickup : : PickupCount ( OldBlock ) ;
if ( OldBlock = = E_BLOCK_LAPIS_ORE )
{
PickupItem . m_ItemHealth = 4 ;
}
if ( cDoors : : IsDoor ( OldBlock ) )
{
PickupItem . m_ItemHealth = 1 ; //For a complete door this works :D
}
}
if ( cRoot : : Get ( ) - > GetPluginManager ( ) - > CallHook ( cPluginManager : : E_PLUGIN_BLOCK_DIG , 2 , a_Packet , m_Player , & PickupItem ) )
{
World - > SendBlockTo ( a_Packet - > m_PosX , a_Packet - > m_PosY , a_Packet - > m_PosZ , m_Player ) ;
return ;
}
2012-03-04 05:54:33 -08:00
int pX = a_Packet - > m_PosX ;
unsigned char pY = a_Packet - > m_PosY ;
int pZ = a_Packet - > m_PosZ ;
2012-02-03 06:33:40 -08:00
2012-03-04 05:54:33 -08:00
AddDirection ( pX , pY , pZ , a_Packet - > m_Direction ) ;
2012-02-03 06:33:40 -08:00
char PossibleBlock = World - > GetBlock ( pX , pY , pZ ) ;
if ( PossibleBlock = = E_BLOCK_FIRE )
{
a_Packet - > m_PosX = pX ;
2012-03-04 05:54:33 -08:00
a_Packet - > m_PosY = pY ;
2012-02-03 06:33:40 -08:00
a_Packet - > m_PosZ = pZ ;
bBroken = true ;
}
if ( ! bBroken )
{
return ;
}
if ( ! World - > DigBlock ( a_Packet - > m_PosX , a_Packet - > m_PosY , a_Packet - > m_PosZ , PickupItem ) )
{
return ;
}
if ( OldBlock = = E_BLOCK_REDSTONE_TORCH_ON )
{
cRedstone Redstone ( World ) ;
Redstone . ChangeRedstone ( a_Packet - > m_PosX , a_Packet - > m_PosY , a_Packet - > m_PosZ , false ) ;
}
if ( OldBlock = = E_BLOCK_REDSTONE_TORCH_OFF )
{
cRedstone Redstone ( World ) ;
Redstone . ChangeRedstone ( a_Packet - > m_PosX , a_Packet - > m_PosY , a_Packet - > m_PosZ , false ) ;
}
if ( OldBlock = = E_BLOCK_REDSTONE_WIRE )
{
cRedstone Redstone ( World ) ;
Redstone . ChangeRedstone ( a_Packet - > m_PosX , a_Packet - > m_PosY , a_Packet - > m_PosZ , false ) ;
}
if ( ( OldBlock = = E_BLOCK_PISTON ) | | ( OldBlock = = E_BLOCK_STICKY_PISTON ) )
{
int newX = a_Packet - > m_PosX ;
int newY = a_Packet - > m_PosY ;
int newZ = a_Packet - > m_PosZ ;
AddPistonDir ( newX , newY , newZ , MetaData & ~ ( 8 ) , 1 ) ;
if ( World - > GetBlock ( newX , newY , newZ ) = = E_BLOCK_PISTON_EXTENSION )
{
World - > SetBlock ( newX , newY , newZ , E_BLOCK_AIR , 0 ) ;
}
}
if ( cDoors : : IsDoor ( OldBlock ) )
{
// Special actions for destroyed door (Destroy second part)
if ( MetaData & 8 )
{
// Was upper part of door
if ( cDoors : : IsDoor ( World - > GetBlock ( a_Packet - > m_PosX , a_Packet - > m_PosY - 1 , a_Packet - > m_PosZ ) ) )
{
World - > SetBlock ( a_Packet - > m_PosX , a_Packet - > m_PosY - 1 , a_Packet - > m_PosZ , E_BLOCK_AIR , 0 ) ;
}
}
else
{
// Was lower part
if ( cDoors : : IsDoor ( World - > GetBlock ( a_Packet - > m_PosX , a_Packet - > m_PosY + 1 , a_Packet - > m_PosZ ) ) )
{
World - > SetBlock ( a_Packet - > m_PosX , a_Packet - > m_PosY + 1 , a_Packet - > m_PosZ , E_BLOCK_AIR , 0 ) ;
}
}
}
m_Player - > UseEquippedItem ( ) ;
}
void cClientHandle : : HandleBlockPlace ( cPacket_BlockPlace * a_Packet )
{
if ( ! CheckBlockInteractionsRate ( ) )
{
return ;
}
cItem & Equipped = m_Player - > GetInventory ( ) . GetEquippedItem ( ) ;
if ( ( Equipped . m_ItemID ! = a_Packet - > m_ItemType ) ) // Not valid
{
LOGWARN ( " Player %s tried to place a block that was not selected! (could indicate bot) " , m_Username . c_str ( ) ) ;
// TODO: We should probably send the current world block to the client, so that it can immediately "let the user know" that they haven't placed the block
return ;
}
if ( cRoot : : Get ( ) - > GetPluginManager ( ) - > CallHook ( cPluginManager : : E_PLUGIN_BLOCK_PLACE , 2 , a_Packet , m_Player ) )
{
if ( a_Packet - > m_Direction > - 1 )
{
AddDirection ( a_Packet - > m_PosX , a_Packet - > m_PosY , a_Packet - > m_PosZ , a_Packet - > m_Direction ) ;
m_Player - > GetWorld ( ) - > SendBlockTo ( a_Packet - > m_PosX , a_Packet - > m_PosY , a_Packet - > m_PosZ , m_Player ) ;
}
return ;
}
//LOG("%i %i %i %i %i %i", a_Packet->m_Count, a_Packet->m_Direction, a_Packet->m_ItemType, a_Packet->m_PosX, a_Packet->m_PosY, a_Packet->m_PosZ);
//printf("Place Dir:%i %i %i %i : %i\n", a_Packet->m_Direction, a_Packet->m_PosX, a_Packet->m_PosY, a_Packet->m_PosZ, a_Packet->m_ItemType);
// 'use' useable items instead of placing blocks
bool bPlaceBlock = true ;
bool UpdateRedstone = false ;
bool AddedCurrent = false ;
if ( a_Packet - > m_Direction > = 0 )
{
ENUM_BLOCK_ID BlockID = ( ENUM_BLOCK_ID ) m_Player - > GetWorld ( ) - > GetBlock ( a_Packet - > m_PosX , a_Packet - > m_PosY , a_Packet - > m_PosZ ) ;
switch ( BlockID )
{
2012-02-15 06:22:44 -08:00
case E_BLOCK_REDSTONE_REPEATER_ON :
case E_BLOCK_REDSTONE_REPEATER_OFF :
2012-02-03 06:33:40 -08:00
{
2012-02-15 06:22:44 -08:00
// no need to update redstone current with a repeater
// TODO: Find meta value of repeater and change it to one step more.
break ;
2012-02-03 06:33:40 -08:00
}
2012-02-15 06:22:44 -08:00
case E_BLOCK_WORKBENCH :
2012-02-03 06:33:40 -08:00
{
bPlaceBlock = false ;
cWindow * Window = new cCraftingWindow ( 0 , true ) ;
m_Player - > OpenWindow ( Window ) ;
2012-02-15 06:22:44 -08:00
break ;
2012-02-03 06:33:40 -08:00
}
2012-02-15 06:22:44 -08:00
case E_BLOCK_FURNACE :
case E_BLOCK_CHEST :
2012-02-03 06:33:40 -08:00
{
bPlaceBlock = false ;
2012-02-15 06:22:44 -08:00
m_Player - > GetWorld ( ) - > UseBlockEntity ( m_Player , a_Packet - > m_PosX , a_Packet - > m_PosY , a_Packet - > m_PosZ ) ;
break ;
2012-02-03 06:33:40 -08:00
}
2012-02-15 06:22:44 -08:00
case E_BLOCK_WOODEN_DOOR :
2012-02-03 06:33:40 -08:00
{
bPlaceBlock = false ;
cDoors : : ChangeDoor ( m_Player - > GetWorld ( ) , a_Packet - > m_PosX , a_Packet - > m_PosY , a_Packet - > m_PosZ ) ;
2012-02-15 06:22:44 -08:00
break ;
2012-02-03 06:33:40 -08:00
}
2012-02-15 06:22:44 -08:00
default :
{
break ;
}
} // switch (BlockID)
} // if (Direction >= 0)
2012-02-03 06:33:40 -08:00
// Some checks to see if it's a placeable item :P
if ( bPlaceBlock )
{
cItem Item ;
Item . m_ItemID = Equipped . m_ItemID ;
Item . m_ItemCount = 1 ;
2012-04-03 07:07:18 -07:00
LOGD ( " Placing item of type %i at {%d, %d, %d} " ,
( int ) a_Packet - > m_ItemType ,
a_Packet - > m_PosX , a_Packet - > m_PosY , a_Packet - > m_PosZ
) ;
2012-02-03 06:33:40 -08:00
// Hacked in edible items go!~
// TODO: Handle hunger
bool bEat = false ;
bool isDoor = false ;
switch ( Item . m_ItemID )
{
2012-02-15 06:22:44 -08:00
case E_ITEM_APPLE :
//m_Player->Heal(4); // 2 hearts
m_Player - > Feed ( 24 ) ; // 2 food bars
bEat = true ;
break ;
case E_ITEM_GOLDEN_APPLE :
//m_Player->Heal(20); // 10 hearts
m_Player - > Feed ( 60 ) ; // 5 food
bEat = true ;
break ;
case E_ITEM_MUSHROOM_SOUP :
///m_Player->Heal(10); // 5 hearts
m_Player - > Feed ( 48 ) ; // 4 food
bEat = true ;
break ;
case E_ITEM_BREAD :
//m_Player->Heal(5); // 2.5 hearts
m_Player - > Feed ( 30 ) ; // 2.5 food
bEat = true ;
break ;
case E_ITEM_RAW_MEAT :
//m_Player->Heal(3); // 1.5 hearts
m_Player - > Feed ( 18 ) ; // 1.5 food
bEat = true ;
break ;
case E_ITEM_COOKED_MEAT :
//m_Player->Heal(8); // 4 hearts
m_Player - > Feed ( 48 ) ; // 4 food
bEat = true ;
break ;
case E_ITEM_RAW_FISH :
//m_Player->Heal(2); // 1 heart
m_Player - > Feed ( 12 ) ; // 1 food
bEat = true ;
break ;
case E_ITEM_COOKED_FISH :
//m_Player->Heal(5); // 2.5 hearts
m_Player - > Feed ( 30 ) ; // 2.5 food
bEat = true ;
break ;
case E_ITEM_RAW_CHICKEN :
//m_Player->Heal(3);
m_Player - > Feed ( 12 ) ; // 1 food
bEat = true ;
break ;
case E_ITEM_COOKED_CHICKEN :
//m_Player->Heal(8);
m_Player - > Feed ( 36 ) ; // 3 food
bEat = true ;
break ;
case E_ITEM_RAW_BEEF :
//m_Player->Heal(3);
m_Player - > Feed ( 18 ) ; // 1.5 food
bEat = true ;
break ;
case E_ITEM_STEAK :
//m_Player->Heal(8);
m_Player - > Feed ( 48 ) ; // 4 food
bEat = true ;
break ;
default :
break ;
2012-02-03 06:33:40 -08:00
} ;
if ( bEat )
{
m_Player - > GetInventory ( ) . RemoveItem ( Item ) ;
return ;
}
if ( a_Packet - > m_Direction < 0 )
{
// clicked in air
return ;
}
//TODO: Wrong Blocks!
int ClickedBlock = ( int ) m_Player - > GetWorld ( ) - > GetBlock ( a_Packet - > m_PosX , a_Packet - > m_PosY , a_Packet - > m_PosZ ) ;
char MetaData = ( char ) Equipped . m_ItemHealth ;
bool LavaBucket = false ;
bool WaterBucket = false ;
bool bRemoveItem = true ;
bool bIgnoreCollision = false ;
if ( ClickedBlock = = E_BLOCK_STEP )
{
if ( MetaData = = m_Player - > GetWorld ( ) - > GetBlockMeta ( a_Packet - > m_PosX , a_Packet - > m_PosY , a_Packet - > m_PosZ ) & & a_Packet - > m_Direction = = 1 ) //only make double slab if meta values are the same and if player clicked on top of the block (Dir = 1)
{
a_Packet - > m_ItemType = E_BLOCK_DOUBLE_STEP ;
a_Packet - > m_PosY - - ;
bIgnoreCollision = true ;
}
}
// Special handling for special items:
switch ( a_Packet - > m_ItemType )
{
case E_ITEM_BUCKET :
{
// TODO: Change this, it is just a small hack to get it working a little bit. seems like the Client sends the position from the first hitable block :s
ClickedBlock = ( int ) m_Player - > GetWorld ( ) - > GetBlock ( a_Packet - > m_PosX , a_Packet - > m_PosY + 1 , a_Packet - > m_PosZ ) ;
LOG ( " Bucket Clicked BlockID: %d " , ClickedBlock ) ;
switch ( ClickedBlock )
{
case E_BLOCK_WATER :
case E_BLOCK_STATIONARY_WATER :
{
WaterBucket = true ;
break ;
}
case E_BLOCK_LAVA :
case E_BLOCK_STATIONARY_LAVA :
{
LavaBucket = true ;
break ;
2011-10-03 11:41:19 -07:00
}
}
2012-02-02 13:13:24 -08:00
break ;
2012-02-03 06:33:40 -08:00
} // case E_ITEM_BUCKET
2012-02-02 13:13:24 -08:00
2012-02-03 06:33:40 -08:00
case E_ITEM_LAVA_BUCKET :
2011-10-03 11:41:19 -07:00
{
2012-02-03 06:33:40 -08:00
if ( ( m_Player - > GetGameMode ( ) = = 1 ) | | ( m_Player - > GetInventory ( ) . RemoveItem ( Item ) ) )
2011-10-03 11:41:19 -07:00
{
2012-02-03 06:33:40 -08:00
a_Packet - > m_ItemType = E_BLOCK_LAVA ;
if ( m_Player - > GetGameMode ( ) = = 1 )
{
break ; //No new Bucket for creative players
}
cItem NewItem ;
NewItem . m_ItemID = E_ITEM_BUCKET ;
NewItem . m_ItemCount = 1 ;
m_Player - > GetInventory ( ) . AddItem ( NewItem ) ;
2011-10-03 11:41:19 -07:00
}
2012-02-02 13:13:24 -08:00
break ;
2012-02-03 06:33:40 -08:00
} // case E_ITEM_LAVA_BUCKET
2012-02-02 13:13:24 -08:00
2012-02-03 06:33:40 -08:00
case E_ITEM_WATER_BUCKET :
2011-10-03 11:41:19 -07:00
{
2012-02-03 06:33:40 -08:00
if ( ( m_Player - > GetGameMode ( ) = = 1 ) | | ( m_Player - > GetInventory ( ) . RemoveItem ( Item ) ) )
2011-10-03 11:41:19 -07:00
{
2012-02-03 06:33:40 -08:00
a_Packet - > m_ItemType = E_BLOCK_WATER ;
if ( m_Player - > GetGameMode ( ) = = 1 )
{
break ; //No new Bucket for creative players
}
cItem NewItem ;
NewItem . m_ItemID = E_ITEM_BUCKET ;
NewItem . m_ItemCount = 1 ;
m_Player - > GetInventory ( ) . AddItem ( NewItem ) ;
2011-10-03 11:41:19 -07:00
}
2012-02-02 13:13:24 -08:00
break ;
2012-02-03 06:33:40 -08:00
}
2012-02-02 13:13:24 -08:00
2012-02-03 06:33:40 -08:00
case E_BLOCK_TORCH :
2011-10-03 11:41:19 -07:00
{
2012-02-03 06:33:40 -08:00
MetaData = cTorch : : DirectionToMetaData ( a_Packet - > m_Direction ) ;
2012-02-02 13:13:24 -08:00
break ;
2012-02-03 06:33:40 -08:00
}
2012-02-02 13:13:24 -08:00
2012-02-03 06:33:40 -08:00
case E_BLOCK_REDSTONE_TORCH_OFF :
2011-10-03 11:41:19 -07:00
{
2012-02-03 06:33:40 -08:00
MetaData = cTorch : : DirectionToMetaData ( a_Packet - > m_Direction ) ;
if ( g_BlockTransparent [ ( int ) m_Player - > GetWorld ( ) - > GetBlock ( a_Packet - > m_PosX , a_Packet - > m_PosY + 2 , a_Packet - > m_PosZ ) ] = = true )
{
//if block above is transparent
//printf("transparent above me\n");
}
else
{
//printf("transparent not above me\n");
}
UpdateRedstone = true ;
AddedCurrent = false ;
2012-02-02 13:13:24 -08:00
break ;
2012-02-03 06:33:40 -08:00
}
2012-02-02 13:13:24 -08:00
2012-02-03 06:33:40 -08:00
case E_BLOCK_REDSTONE_TORCH_ON :
2011-10-03 11:41:19 -07:00
{
2012-02-03 06:33:40 -08:00
MetaData = cTorch : : DirectionToMetaData ( a_Packet - > m_Direction ) ;
UpdateRedstone = true ;
AddedCurrent = true ;
2012-02-02 13:13:24 -08:00
break ;
2012-02-03 06:33:40 -08:00
}
2012-02-02 13:13:24 -08:00
2012-02-03 06:33:40 -08:00
case E_ITEM_REDSTONE_DUST :
2011-10-03 11:41:19 -07:00
{
2012-02-03 06:33:40 -08:00
MetaData = 0 ;
a_Packet - > m_ItemType = E_BLOCK_REDSTONE_WIRE ;
UpdateRedstone = true ;
AddedCurrent = false ;
break ;
}
case E_ITEM_REDSTONE_REPEATER :
{
MetaData = cRedstone : : RepeaterRotationToMetaData ( m_Player - > GetRotation ( ) ) ;
a_Packet - > m_ItemType = E_BLOCK_REDSTONE_REPEATER_OFF ;
UpdateRedstone = true ;
AddedCurrent = false ;
break ;
}
2011-10-03 11:41:19 -07:00
2012-02-03 06:33:40 -08:00
case E_BLOCK_PISTON :
case E_BLOCK_STICKY_PISTON :
{
MetaData = cPiston : : RotationPitchToMetaData ( m_Player - > GetRotation ( ) , m_Player - > GetPitch ( ) ) ;
UpdateRedstone = true ;
AddedCurrent = false ;
break ;
}
case E_ITEM_IRON_DOOR :
{
a_Packet - > m_ItemType = E_BLOCK_IRON_DOOR ;
MetaData = cDoors : : RotationToMetaData ( m_Player - > GetRotation ( ) ) ;
isDoor = true ;
2012-02-02 13:13:24 -08:00
break ;
2012-02-03 06:33:40 -08:00
}
2012-02-02 13:13:24 -08:00
2012-02-03 06:33:40 -08:00
case E_ITEM_WOODEN_DOOR :
2011-10-03 11:41:19 -07:00
{
2012-02-03 06:33:40 -08:00
a_Packet - > m_ItemType = E_BLOCK_WOODEN_DOOR ;
MetaData = cDoors : : RotationToMetaData ( m_Player - > GetRotation ( ) ) ;
isDoor = true ;
2012-02-02 13:13:24 -08:00
break ;
2012-02-03 06:33:40 -08:00
}
2012-02-02 13:13:24 -08:00
2012-02-03 06:33:40 -08:00
case E_BLOCK_CHEST :
case E_BLOCK_FURNACE :
case E_BLOCK_DISPENSER :
2011-10-03 11:41:19 -07:00
{
2012-02-03 06:33:40 -08:00
MetaData = cPiston : : RotationPitchToMetaData ( m_Player - > GetRotation ( ) , 0 ) ; // Same orientation as pistons, just ignore pitch
break ;
}
case E_BLOCK_COBBLESTONE_STAIRS :
case E_BLOCK_BRICK_STAIRS :
case E_BLOCK_STONE_BRICK_STAIRS :
case E_BLOCK_NETHER_BRICK_STAIRS :
case E_BLOCK_WOODEN_STAIRS :
{
MetaData = cStairs : : RotationToMetaData ( m_Player - > GetRotation ( ) ) ;
break ;
}
case E_BLOCK_LADDER :
{
MetaData = cLadder : : DirectionToMetaData ( a_Packet - > m_Direction ) ;
break ;
}
case E_ITEM_SIGN :
{
LOG ( " Sign Dir: %i " , a_Packet - > m_Direction ) ;
if ( a_Packet - > m_Direction = = 1 )
2011-10-03 11:41:19 -07:00
{
2012-02-03 06:33:40 -08:00
LOG ( " Player Rotation: %f " , m_Player - > GetRotation ( ) ) ;
MetaData = cSign : : RotationToMetaData ( m_Player - > GetRotation ( ) ) ;
LOG ( " Sign rotation %i " , MetaData ) ;
a_Packet - > m_ItemType = E_BLOCK_SIGN_POST ;
2011-10-03 11:41:19 -07:00
}
else
{
2012-02-03 06:33:40 -08:00
MetaData = cSign : : DirectionToMetaData ( a_Packet - > m_Direction ) ;
a_Packet - > m_ItemType = E_BLOCK_WALLSIGN ;
2011-10-03 11:41:19 -07:00
}
2012-02-02 13:13:24 -08:00
break ;
2012-02-03 06:33:40 -08:00
}
case E_ITEM_FLINT_AND_STEEL :
2011-10-03 11:41:19 -07:00
{
2012-02-03 06:33:40 -08:00
a_Packet - > m_ItemType = E_ITEM_FIRE ;
m_Player - > UseEquippedItem ( ) ;
bRemoveItem = false ;
2012-02-02 13:13:24 -08:00
break ;
2012-02-03 06:33:40 -08:00
}
default :
2011-10-03 11:41:19 -07:00
{
2012-02-03 06:33:40 -08:00
break ;
}
} // switch (a_Packet->m_ItemType)
if ( LavaBucket )
{
if ( ( m_Player - > GetGameMode ( ) = = 1 ) | | ( m_Player - > GetInventory ( ) . RemoveItem ( Item ) ) ) {
cItem NewItem ;
NewItem . m_ItemID = E_ITEM_LAVA_BUCKET ;
NewItem . m_ItemCount = 1 ;
m_Player - > GetInventory ( ) . AddItem ( NewItem ) ;
m_Player - > GetWorld ( ) - > SetBlock ( a_Packet - > m_PosX , a_Packet - > m_PosY + 1 , a_Packet - > m_PosZ , E_BLOCK_AIR , 0 ) ;
}
}
else if ( WaterBucket )
{
if ( ( m_Player - > GetGameMode ( ) = = 1 ) | | ( m_Player - > GetInventory ( ) . RemoveItem ( Item ) ) )
{
cItem NewItem ;
NewItem . m_ItemID = E_ITEM_WATER_BUCKET ;
NewItem . m_ItemCount = 1 ;
m_Player - > GetInventory ( ) . AddItem ( NewItem ) ;
m_Player - > GetWorld ( ) - > SetBlock ( a_Packet - > m_PosX , a_Packet - > m_PosY + 1 , a_Packet - > m_PosZ , E_BLOCK_AIR , 0 ) ;
}
}
else if ( IsValidBlock ( a_Packet - > m_ItemType ) )
{
int X = a_Packet - > m_PosX ;
2012-03-04 05:54:33 -08:00
int Y = a_Packet - > m_PosY ;
2012-02-03 06:33:40 -08:00
int Z = a_Packet - > m_PosZ ;
AddDirection ( X , Y , Z , a_Packet - > m_Direction ) ;
int PlaceBlock = m_Player - > GetWorld ( ) - > GetBlock ( X , Y , Z ) ;
if (
( PlaceBlock ! = E_BLOCK_AIR )
& & ( PlaceBlock ! = E_BLOCK_WATER )
& & ( PlaceBlock ! = E_BLOCK_STATIONARY_WATER )
& & ( PlaceBlock ! = E_BLOCK_LAVA )
& & ( PlaceBlock ! = E_BLOCK_STATIONARY_LAVA )
& & ! bIgnoreCollision
)
{
//tried to place a block *into* another?
return ; // happens when you place a block aiming at side of block like torch or stem
}
// Check whether selected item is allowed to be placed on specific surface
bool bIllegalSurface = false ;
ENUM_BLOCK_ID SurfaceBlock = ( ENUM_BLOCK_ID ) m_Player - > GetWorld ( ) - > GetBlock ( X , Y - 1 , Z ) ;
switch ( a_Packet - > m_ItemType )
{
case E_BLOCK_YELLOW_FLOWER : // Can ONLY be placed on dirt/grass
case E_BLOCK_RED_ROSE :
case E_BLOCK_SAPLING :
2011-10-03 11:41:19 -07:00
{
2012-02-03 06:33:40 -08:00
switch ( SurfaceBlock )
2011-10-03 11:41:19 -07:00
{
2012-02-03 06:33:40 -08:00
case E_BLOCK_DIRT :
case E_BLOCK_GRASS :
{
bIllegalSurface = false ;
break ;
}
default :
{
bIllegalSurface = true ;
break ;
}
} ;
break ;
}
case E_BLOCK_BROWN_MUSHROOM : // Can be placed on pretty much anything, with exceptions
case E_BLOCK_RED_MUSHROOM :
{
switch ( SurfaceBlock )
{
case E_BLOCK_GLASS :
case E_BLOCK_YELLOW_FLOWER :
case E_BLOCK_RED_ROSE :
case E_BLOCK_BROWN_MUSHROOM :
case E_BLOCK_RED_MUSHROOM :
case E_BLOCK_CACTUS :
{
bIllegalSurface = true ;
break ;
}
2011-10-03 11:41:19 -07:00
}
2012-02-03 06:33:40 -08:00
break ;
2011-10-03 11:41:19 -07:00
}
2012-02-03 06:33:40 -08:00
case E_BLOCK_CACTUS :
{
bIllegalSurface = ( SurfaceBlock ! = E_BLOCK_SAND ) & & ( SurfaceBlock ! = E_BLOCK_CACTUS ) ; // Cactus can only be placed on sand and itself
// Check surroundings. Cacti may ONLY be surrounded by air
cWorld * World = m_Player - > GetWorld ( ) ;
if (
( World - > GetBlock ( X - 1 , Y , Z ) ! = E_BLOCK_AIR ) | |
( World - > GetBlock ( X + 1 , Y , Z ) ! = E_BLOCK_AIR ) | |
( World - > GetBlock ( X , Y , Z - 1 ) ! = E_BLOCK_AIR ) | |
( World - > GetBlock ( X , Y , Z + 1 ) ! = E_BLOCK_AIR )
)
{
bIllegalSurface = true ;
}
break ;
}
} // switch (a_Packet->m_ItemType)
if ( bIllegalSurface )
2011-10-03 11:41:19 -07:00
{
2012-02-03 06:33:40 -08:00
return ;
}
if ( bRemoveItem )
2011-10-03 11:41:19 -07:00
{
2012-02-03 06:33:40 -08:00
if ( ( m_Player - > GetGameMode ( ) ! = 1 ) & & ! m_Player - > GetInventory ( ) . RemoveItem ( Item ) )
2011-10-03 11:41:19 -07:00
{
2012-02-03 06:33:40 -08:00
return ;
2011-10-03 11:41:19 -07:00
}
2012-02-03 06:33:40 -08:00
}
if ( isDoor )
2011-12-27 10:39:06 -08:00
{
2012-02-03 06:33:40 -08:00
if ( ( m_Player - > GetWorld ( ) - > GetBlock ( X , Y + 1 , Z ) = = E_BLOCK_AIR ) | | ( m_Player - > GetWorld ( ) - > GetBlock ( X , Y + 1 , Z ) = = E_BLOCK_AIR ) )
2011-12-27 10:39:06 -08:00
{
2012-02-03 06:33:40 -08:00
m_Player - > GetWorld ( ) - > SetBlock ( X , Y + 1 , Z , ( char ) a_Packet - > m_ItemType , MetaData + 8 ) ;
m_Player - > GetWorld ( ) - > SetBlock ( X , Y , Z , ( char ) a_Packet - > m_ItemType , MetaData ) ;
2011-12-27 10:39:06 -08:00
}
2012-02-03 06:33:40 -08:00
}
else
{
m_Player - > GetWorld ( ) - > SetBlock ( X , Y , Z , ( char ) a_Packet - > m_ItemType , MetaData ) ;
}
if ( UpdateRedstone )
{
cRedstone Redstone ( m_Player - > GetWorld ( ) ) ;
Redstone . ChangeRedstone ( a_Packet - > m_PosX , a_Packet - > m_PosY + 1 , a_Packet - > m_PosZ , AddedCurrent ) ;
}
}
}
/*
// FakeTruth's "magic stick of removal" :D
// TODO: Turn this into a plugin
if ( m_Username . compare ( " FakeTruth " ) = = 0 )
{
if ( a_Packet - > m_ItemType = = 280 )
{
cRoot : : Get ( ) - > GetWorld ( ) - > SetBlock ( a_Packet - > m_PosX , a_Packet - > m_PosY , a_Packet - > m_PosZ , 0 , 0 ) ;
}
}
*/
}
void cClientHandle : : HandlePickupSpawn ( cPacket_PickupSpawn * a_Packet )
{
LOG ( " Received packet E_PICKUP_SPAWN " ) ;
cItem DroppedItem ;
DroppedItem . m_ItemID = ( ENUM_ITEM_ID ) a_Packet - > m_Item ;
DroppedItem . m_ItemCount = a_Packet - > m_Count ;
DroppedItem . m_ItemHealth = 0x0 ; // TODO: Somehow figure out what item was dropped, and apply correct health
if ( m_Player - > GetInventory ( ) . RemoveItem ( DroppedItem ) )
{
cPickup * Pickup = new cPickup ( a_Packet ) ;
Pickup - > Initialize ( m_Player - > GetWorld ( ) ) ;
}
}
void cClientHandle : : HandleChat ( cPacket_Chat * a_Packet )
{
if ( ! cRoot : : Get ( ) - > GetServer ( ) - > Command ( * this , a_Packet - > m_Message . c_str ( ) ) )
{
a_Packet - > m_Message . insert ( 0 , " < " + m_Player - > GetColor ( ) + m_Username + cChatColor : : White + " > " ) ;
cRoot : : Get ( ) - > GetServer ( ) - > Broadcast ( * a_Packet ) ;
}
}
void cClientHandle : : HandlePlayerLook ( cPacket_PlayerLook * a_Packet )
{
m_Player - > SetRotation ( a_Packet - > m_Rotation ) ;
m_Player - > SetPitch ( a_Packet - > m_Pitch ) ;
m_Player - > SetTouchGround ( a_Packet - > m_bFlying ) ;
m_Player - > WrapRotation ( ) ;
}
void cClientHandle : : HandlePlayerMoveLook ( cPacket_PlayerMoveLook * a_Packet )
{
m_Player - > MoveTo ( Vector3d ( a_Packet - > m_PosX , a_Packet - > m_PosY , a_Packet - > m_PosZ ) ) ;
m_Player - > SetStance ( a_Packet - > m_Stance ) ;
m_Player - > SetTouchGround ( a_Packet - > m_bFlying ) ;
m_Player - > SetRotation ( a_Packet - > m_Rotation ) ;
m_Player - > SetPitch ( a_Packet - > m_Pitch ) ;
m_Player - > WrapRotation ( ) ;
}
void cClientHandle : : HandleAnimation ( cPacket_ArmAnim * a_Packet )
{
a_Packet - > m_EntityID = m_Player - > GetUniqueID ( ) ;
cRoot : : Get ( ) - > GetServer ( ) - > Broadcast ( * a_Packet , this ) ;
}
void cClientHandle : : HandleItemSwitch ( cPacket_ItemSwitch * a_Packet )
{
m_Player - > GetInventory ( ) . SetEquippedSlot ( a_Packet - > m_SlotNum ) ;
cPacket_EntityEquipment Equipment ;
Equipment . m_ItemID = ( short ) m_Player - > GetInventory ( ) . GetEquippedItem ( ) . m_ItemID ;
Equipment . m_Slot = 0 ;
Equipment . m_UniqueID = m_Player - > GetUniqueID ( ) ;
cRoot : : Get ( ) - > GetServer ( ) - > Broadcast ( Equipment , this ) ;
}
void cClientHandle : : HandleWindowClose ( cPacket_WindowClose * a_Packet )
{
m_Player - > CloseWindow ( a_Packet - > m_Close ) ;
}
void cClientHandle : : HandleWindowClick ( cPacket_WindowClick * a_Packet )
{
if ( a_Packet - > m_WindowID = = 0 )
{
m_Player - > GetInventory ( ) . Clicked ( a_Packet ) ;
return ;
}
cWindow * Window = m_Player - > GetWindow ( ) ;
if ( Window = = NULL )
{
LOGWARNING ( " Player \" %s \" clicked in a non-existent window. Ignoring " , m_Username . c_str ( ) ) ;
return ;
}
Window - > Clicked ( a_Packet , * m_Player ) ;
}
void cClientHandle : : HandleUpdateSign ( cPacket_UpdateSign * a_Packet )
{
cWorld * World = m_Player - > GetWorld ( ) ;
2012-02-21 08:27:30 -08:00
World - > UpdateSign ( a_Packet - > m_PosX , a_Packet - > m_PosY , a_Packet - > m_PosZ , a_Packet - > m_Line1 , a_Packet - > m_Line2 , a_Packet - > m_Line3 , a_Packet - > m_Line4 ) ;
2012-02-03 06:33:40 -08:00
}
void cClientHandle : : HandleUseEntity ( cPacket_UseEntity * a_Packet )
{
if ( ! a_Packet - > m_bLeftClick )
{
return ;
}
2012-02-16 09:20:28 -08:00
class cDamageEntity : public cEntityCallback
2012-02-03 06:33:40 -08:00
{
2012-02-16 09:20:28 -08:00
virtual bool Item ( cEntity * a_Entity ) override
{
if ( a_Entity - > IsA ( " cPawn " ) )
{
reinterpret_cast < cPawn * > ( a_Entity ) - > TakeDamage ( Damage , Instigator ) ;
}
return true ;
}
public :
int Damage ;
cEntity * Instigator ;
} Callback ;
Callback . Damage = 1 ; // TODO: Find proper damage from current item equipped
Callback . Instigator = m_Player ;
cWorld * World = m_Player - > GetWorld ( ) ;
World - > DoWithEntity ( a_Packet - > m_TargetID , Callback ) ;
2012-02-03 06:33:40 -08:00
}
void cClientHandle : : HandleRespawn ( void )
{
m_Player - > Respawn ( ) ;
}
void cClientHandle : : HandleDisconnect ( cPacket_Disconnect * a_Packet )
{
LOG ( " Received d/c packet from \" %s \" " , m_Username . c_str ( ) ) ;
if ( ! cRoot : : Get ( ) - > GetPluginManager ( ) - > CallHook ( cPluginManager : : E_PLUGIN_DISCONNECT , 2 , a_Packet - > m_Reason . c_str ( ) , m_Player ) )
{
cPacket_Chat DisconnectMessage ( m_Username + " disconnected: " + a_Packet - > m_Reason ) ;
cRoot : : Get ( ) - > GetServer ( ) - > Broadcast ( DisconnectMessage ) ;
}
Destroy ( ) ;
}
void cClientHandle : : HandleKeepAlive ( cPacket_KeepAlive * a_Packet )
{
if ( a_Packet - > m_KeepAliveID = = m_PingID )
{
cTimer t1 ;
m_Ping = ( short ) ( ( t1 . GetNowTime ( ) - m_PingStartTime ) / 2 ) ;
}
}
bool cClientHandle : : CheckBlockInteractionsRate ( void )
{
2012-03-10 14:27:24 -08:00
ASSERT ( m_Player ! = NULL ) ;
ASSERT ( m_Player - > GetWorld ( ) ! = NULL ) ;
2012-02-03 06:33:40 -08:00
int LastActionCnt = m_Player - > GetLastBlockActionCnt ( ) ;
2012-03-10 14:27:24 -08:00
if ( ( m_Player - > GetWorld ( ) - > GetTime ( ) - m_Player - > GetLastBlockActionTime ( ) ) < 0.1 )
2012-02-03 06:33:40 -08:00
{
// Limit the number of block interactions per tick
m_Player - > SetLastBlockActionTime ( ) ; //Player tried to interact with a block. Reset last block interation time.
m_Player - > SetLastBlockActionCnt ( LastActionCnt + 1 ) ;
if ( m_Player - > GetLastBlockActionCnt ( ) > MAXBLOCKCHANGEINTERACTIONS )
{
// Kick if more than MAXBLOCKCHANGEINTERACTIONS per tick
LOGWARN ( " Player %s tried to interact with a block too quickly! (could indicate bot) Was Kicked. " , m_Username . c_str ( ) ) ;
Kick ( " You're a baaaaaad boy! " ) ;
return false ;
}
}
else
{
m_Player - > SetLastBlockActionCnt ( 0 ) ; // Reset count
m_Player - > SetLastBlockActionTime ( ) ; // Player tried to interact with a block. Reset last block interation time.
}
return true ;
2011-10-03 11:41:19 -07:00
}
2012-01-31 10:06:24 -08:00
2011-10-03 11:41:19 -07:00
void cClientHandle : : Tick ( float a_Dt )
{
( void ) a_Dt ;
2012-02-02 13:13:24 -08:00
if ( cWorld : : GetTime ( ) - m_TimeLastPacket > 30.f ) // 30 seconds time-out
2011-10-03 11:41:19 -07:00
{
cPacket_Disconnect DC ( " Nooooo!! You timed out! D: Come back! " ) ;
2012-02-07 12:49:52 -08:00
m_Socket . Send ( & DC ) ;
2011-10-03 11:41:19 -07:00
2012-02-13 13:47:03 -08:00
// TODO: Cannot sleep in the tick thread!
2012-02-02 13:13:24 -08:00
cSleep : : MilliSleep ( 1000 ) ; // Give packet some time to be received
2011-10-03 11:41:19 -07:00
Destroy ( ) ;
}
2011-12-27 10:39:06 -08:00
cTimer t1 ;
// Send ping packet
2012-02-02 13:13:24 -08:00
if ( m_LastPingTime + cClientHandle : : PING_TIME_MS < = t1 . GetNowTime ( ) )
{
2012-02-13 13:47:03 -08:00
m_PingID + + ;
2011-12-27 10:39:06 -08:00
cPacket_KeepAlive Ping ( m_PingID ) ;
m_PingStartTime = t1 . GetNowTime ( ) ;
Send ( Ping ) ;
m_LastPingTime = m_PingStartTime ;
}
2011-10-03 11:41:19 -07:00
}
2012-01-31 10:06:24 -08:00
2012-03-10 13:34:47 -08:00
void cClientHandle : : Send ( const cPacket & a_Packet , ENUM_PRIORITY a_Priority /* = E_PRIORITY_NORMAL */ )
2011-10-03 11:41:19 -07:00
{
2012-02-03 06:33:40 -08:00
if ( m_bKicking ) return ; // Don't add more packets if player is getting kicked anyway
2011-10-03 11:41:19 -07:00
2012-02-13 13:47:03 -08:00
// If it is the packet spawning myself for myself, drop it silently:
2012-03-10 13:34:47 -08:00
if ( a_Packet . m_PacketID = = E_NAMED_ENTITY_SPAWN )
2012-02-13 13:47:03 -08:00
{
2012-03-10 13:34:47 -08:00
if ( ( ( cPacket_NamedEntitySpawn & ) a_Packet ) . m_UniqueID = = m_Player - > GetUniqueID ( ) )
2012-02-13 13:47:03 -08:00
{
return ;
}
}
// Filter out packets that don't belong to a csDownloadingWorld state:
if ( m_State = = csDownloadingWorld )
{
2012-03-10 13:34:47 -08:00
switch ( a_Packet . m_PacketID )
2012-02-13 13:47:03 -08:00
{
case E_PLAYERMOVELOOK :
case E_KEEP_ALIVE :
case E_PRE_CHUNK :
2012-03-05 08:41:57 -08:00
case E_MAP_CHUNK :
2012-02-13 13:47:03 -08:00
{
// Allow
break ;
}
2012-03-05 08:41:57 -08:00
default : return ;
}
}
// Check chunks being sent, erase them from m_ChunksToSend:
2012-03-10 13:34:47 -08:00
if ( a_Packet . m_PacketID = = E_MAP_CHUNK )
2012-03-05 08:41:57 -08:00
{
2012-03-10 13:34:47 -08:00
int ChunkX = ( ( cPacket_MapChunk & ) a_Packet ) . m_PosX ;
int ChunkZ = ( ( cPacket_MapChunk & ) a_Packet ) . m_PosZ ;
2012-03-11 03:48:20 -07:00
bool Found = false ;
2012-03-05 08:41:57 -08:00
cCSLock Lock ( m_CSChunkLists ) ;
for ( cChunkCoordsList : : iterator itr = m_ChunksToSend . begin ( ) ; itr ! = m_ChunksToSend . end ( ) ; + + itr )
{
if ( ( itr - > m_ChunkX = = ChunkX ) & & ( itr - > m_ChunkZ = = ChunkZ ) )
2012-02-13 13:47:03 -08:00
{
2012-03-05 08:41:57 -08:00
m_ChunksToSend . erase ( itr ) ;
2012-02-13 13:47:03 -08:00
CheckIfWorldDownloaded ( ) ;
2012-03-11 03:48:20 -07:00
Found = true ;
2012-02-13 13:47:03 -08:00
break ;
}
2012-03-05 08:41:57 -08:00
} // for itr - m_ChunksToSend[]
2012-03-11 03:48:20 -07:00
if ( ! Found )
{
return ;
}
2012-02-13 13:47:03 -08:00
}
2012-03-05 08:41:57 -08:00
// Optimize away multiple queued RelativeEntityMoveLook packets:
2012-02-25 16:36:51 -08:00
cCSLock Lock ( m_CSPackets ) ;
2012-02-02 13:13:24 -08:00
if ( a_Priority = = E_PRIORITY_NORMAL )
2011-10-03 11:41:19 -07:00
{
2012-03-10 13:34:47 -08:00
if ( a_Packet . m_PacketID = = E_REL_ENT_MOVE_LOOK )
2011-10-03 11:41:19 -07:00
{
2012-02-03 06:33:40 -08:00
PacketList & Packets = m_PendingNrmSendPackets ;
2012-03-10 13:34:47 -08:00
const cPacket_RelativeEntityMoveLook & ThisPacketData = reinterpret_cast < const cPacket_RelativeEntityMoveLook & > ( a_Packet ) ;
2012-02-02 13:13:24 -08:00
for ( PacketList : : iterator itr = Packets . begin ( ) ; itr ! = Packets . end ( ) ; + + itr )
2011-10-03 11:41:19 -07:00
{
bool bBreak = false ;
2012-02-02 13:13:24 -08:00
switch ( ( * itr ) - > m_PacketID )
2011-10-03 11:41:19 -07:00
{
2012-02-02 13:13:24 -08:00
case E_REL_ENT_MOVE_LOOK :
2011-10-03 11:41:19 -07:00
{
2012-03-10 13:34:47 -08:00
cPacket_RelativeEntityMoveLook * PacketData = reinterpret_cast < cPacket_RelativeEntityMoveLook * > ( * itr ) ;
if ( ThisPacketData . m_UniqueID = = PacketData - > m_UniqueID )
2011-10-03 11:41:19 -07:00
{
2012-02-03 06:33:40 -08:00
Packets . erase ( itr ) ;
2011-10-03 11:41:19 -07:00
bBreak = true ;
2011-10-26 16:18:31 -07:00
delete PacketData ;
2011-10-03 11:41:19 -07:00
break ;
}
2012-02-02 13:13:24 -08:00
break ;
} // case E_REL_END_MOVE_LOOK
} // switch (*itr -> Packet type)
if ( bBreak )
{
2011-10-03 11:41:19 -07:00
break ;
}
2012-02-02 13:13:24 -08:00
} // for itr - Packets[]
} // if (E_REL_ENT_MOVE_LOOK
2012-03-10 13:34:47 -08:00
m_PendingNrmSendPackets . push_back ( a_Packet . Clone ( ) ) ;
2012-02-02 13:13:24 -08:00
}
2012-02-03 06:33:40 -08:00
else if ( a_Priority = = E_PRIORITY_LOW )
2012-02-02 13:13:24 -08:00
{
2012-03-10 13:34:47 -08:00
m_PendingLowSendPackets . push_back ( a_Packet . Clone ( ) ) ;
2012-02-02 13:13:24 -08:00
}
Lock . Unlock ( ) ;
2012-02-25 16:36:51 -08:00
// Notify SocketThreads that we have something to write:
cRoot : : Get ( ) - > GetServer ( ) - > NotifyClientWrite ( this ) ;
2011-10-03 11:41:19 -07:00
}
2012-01-31 10:06:24 -08:00
2012-02-13 13:47:03 -08:00
void cClientHandle : : CheckIfWorldDownloaded ( void )
{
if ( m_State ! = csDownloadingWorld )
{
return ;
}
cCSLock Lock ( m_CSChunkLists ) ;
2012-03-02 06:09:52 -08:00
if ( m_ChunksToSend . empty ( ) )
2012-02-13 13:47:03 -08:00
{
SendConfirmPosition ( ) ;
}
}
void cClientHandle : : SendConfirmPosition ( void )
{
2012-02-20 08:39:00 -08:00
LOG ( " Spawning player \" %s \" at {%.2f, %.2f, %.2f} " ,
m_Username . c_str ( ) , m_Player - > GetPosX ( ) , m_Player - > GetPosY ( ) , m_Player - > GetPosZ ( )
) ;
2012-02-13 13:47:03 -08:00
m_State = csConfirmingPos ;
if ( ! cRoot : : Get ( ) - > GetPluginManager ( ) - > CallHook ( cPluginManager : : E_PLUGIN_PLAYER_JOIN , 1 , m_Player ) )
{
// Broadcast that this player has joined the game! Yay~
cPacket_Chat Joined ( m_Username + " joined the game! " ) ;
cRoot : : Get ( ) - > GetServer ( ) - > Broadcast ( Joined , this ) ;
}
m_ConfirmPosition = m_Player - > GetPosition ( ) ;
Send ( cPacket_PlayerMoveLook ( m_Player ) ) ;
}
2012-02-08 02:02:46 -08:00
const AString & cClientHandle : : GetUsername ( void ) const
2011-10-03 11:41:19 -07:00
{
2012-02-08 02:02:46 -08:00
return m_Username ;
}
2011-10-03 11:41:19 -07:00
2012-02-23 14:51:03 -08: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 ( ) ;
}
2012-03-11 03:48:20 -07:00
bool cClientHandle : : WantsSendChunk ( int a_ChunkX , int a_ChunkY , int a_ChunkZ )
{
cCSLock Lock ( m_CSChunkLists ) ;
return ( std : : find ( m_ChunksToSend . begin ( ) , m_ChunksToSend . end ( ) , cChunkCoords ( a_ChunkX , a_ChunkY , a_ChunkZ ) ) ! = m_ChunksToSend . end ( ) ) ;
}
2012-02-08 02:02:46 -08:00
void cClientHandle : : DataReceived ( const char * a_Data , int a_Size )
{
// Data is received from the client
m_ReceivedData . append ( a_Data , a_Size ) ;
// Parse and handle all complete packets in m_ReceivedData:
while ( ! m_ReceivedData . empty ( ) )
2011-10-03 11:41:19 -07:00
{
2012-02-08 02:02:46 -08:00
cPacket * pPacket = m_PacketMap [ ( unsigned char ) m_ReceivedData [ 0 ] ] ;
if ( pPacket = = NULL )
{
LOGERROR ( " Unknown packet type 0x%02x from client \" %s \" " , ( unsigned char ) m_ReceivedData [ 0 ] , m_Username . c_str ( ) ) ;
AString Reason ;
2012-03-24 06:28:53 -07:00
Printf ( Reason , " [C->S] Unknown PacketID: 0x%02x " , ( unsigned char ) m_ReceivedData [ 0 ] ) ;
2012-02-08 02:02:46 -08:00
cPacket_Disconnect DC ( Reason ) ;
m_Socket . Send ( & DC ) ;
cSleep : : MilliSleep ( 1000 ) ; // Give packet some time to be received
Destroy ( ) ;
return ;
}
int NumBytes = pPacket - > Parse ( m_ReceivedData . data ( ) + 1 , m_ReceivedData . size ( ) - 1 ) ;
if ( NumBytes = = PACKET_ERROR )
2011-10-03 11:41:19 -07:00
{
2012-02-08 02:02:46 -08:00
LOGERROR ( " Protocol error while parsing packet type 0x%02x; disconnecting client \" %s \" " , ( unsigned char ) m_ReceivedData [ 0 ] , m_Username . c_str ( ) ) ;
cPacket_Disconnect DC ( " Protocol error " ) ;
m_Socket . Send ( & DC ) ;
cSleep : : MilliSleep ( 1000 ) ; // Give packet some time to be received
Destroy ( ) ;
return ;
}
else if ( NumBytes = = PACKET_INCOMPLETE )
{
// Not a complete packet
2011-10-03 11:41:19 -07:00
break ;
}
2012-02-08 02:02:46 -08:00
else
2011-10-03 11:41:19 -07:00
{
2012-02-08 02:02:46 -08:00
// Packet parsed successfully, add it to internal queue:
HandlePacket ( pPacket ) ;
// Erase the packet from the buffer:
2012-02-19 15:00:00 -08:00
ASSERT ( m_ReceivedData . size ( ) > ( size_t ) NumBytes ) ;
2012-02-08 02:02:46 -08:00
m_ReceivedData . erase ( 0 , NumBytes + 1 ) ;
}
} // while (!Received.empty())
2011-10-03 11:41:19 -07:00
}
2012-01-31 10:06:24 -08:00
2012-02-08 02:02:46 -08:00
void cClientHandle : : GetOutgoingData ( AString & a_Data )
2011-10-03 11:41:19 -07:00
{
2012-02-08 02:02:46 -08:00
// Data can be sent to client
2012-02-25 16:36:51 -08:00
cCSLock Lock ( m_CSPackets ) ;
if ( m_PendingNrmSendPackets . size ( ) + m_PendingLowSendPackets . size ( ) > MAX_OUTGOING_PACKETS )
{
LOGERROR ( " ERROR: Too many packets in queue for player %s !! " , m_Username . c_str ( ) ) ;
cPacket_Disconnect DC ( " Too many packets in queue. " ) ;
m_Socket . Send ( DC ) ;
Lock . Unlock ( ) ;
Destroy ( ) ;
return ;
}
if ( ( m_PendingNrmSendPackets . size ( ) = = 0 ) & & ( m_PendingLowSendPackets . size ( ) = = 0 ) )
{
return ;
}
if ( m_PendingNrmSendPackets . size ( ) > MAX_OUTGOING_PACKETS / 2 )
{
LOGINFO ( " Suspiciously many pending packets: %i; client \" %s \" , LastType: 0x%02x " , m_PendingNrmSendPackets . size ( ) , m_Username . c_str ( ) , ( * m_PendingNrmSendPackets . rbegin ( ) ) - > m_PacketID ) ;
}
AString Data ;
if ( ! m_PendingNrmSendPackets . empty ( ) )
{
m_PendingNrmSendPackets . front ( ) - > Serialize ( Data ) ;
delete m_PendingNrmSendPackets . front ( ) ;
m_PendingNrmSendPackets . erase ( m_PendingNrmSendPackets . begin ( ) ) ;
}
else if ( ! m_PendingLowSendPackets . empty ( ) )
{
m_PendingLowSendPackets . front ( ) - > Serialize ( Data ) ;
delete m_PendingLowSendPackets . front ( ) ;
m_PendingLowSendPackets . erase ( m_PendingLowSendPackets . begin ( ) ) ;
}
Lock . Unlock ( ) ;
a_Data . append ( Data ) ;
// Disconnect player after all packets have been sent
if ( m_bKicking & & ( m_PendingNrmSendPackets . size ( ) + m_PendingLowSendPackets . size ( ) = = 0 ) )
{
Destroy ( ) ;
}
2011-10-03 11:41:19 -07:00
}
2012-01-31 10:06:24 -08:00
2012-02-08 02:02:46 -08:00
void cClientHandle : : SocketClosed ( void )
2011-10-03 11:41:19 -07:00
{
2012-02-08 02:02:46 -08:00
// The socket has been closed for any reason
// TODO
/*
self - > Destroy ( ) ;
LOG ( " Client \" %s \" disconnected " , GetLogName ( ) . c_str ( ) ) ;
*/
2011-10-25 20:22:43 -07:00
}
2012-01-31 10:06:24 -08:00