2012-01-29 19:28:19 +00:00
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
2011-10-03 18:41:19 +00: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"
2012-06-03 23:35:17 +00:00
#include "cStep.h"
2011-11-10 02:05:51 +00:00
#include "cDoors.h"
2011-10-03 18:41:19 +00:00
#include "cLadder.h"
#include "cSign.h"
2011-11-04 05:01:55 +00:00
#include "cRedstone.h"
2011-11-05 18:28:19 +00:00
#include "cPiston.h"
2011-10-03 18:41:19 +00:00
#include "cBlockToPickup.h"
#include "cMonster.h"
#include "cChatColor.h"
#include "cSocket.h"
2011-12-27 18:39:06 +00:00
#include "cTimer.h"
2011-10-03 18:41:19 +00: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 18:39:06 +00:00
#include "MersenneTwister.h"
2011-10-03 18:41:19 +00: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"
2012-01-01 04:55:17 +00:00
#include "packets/cPacket_CreativeInventoryAction.h"
2011-11-02 20:40:39 +00:00
#include "packets/cPacket_NewInvalidState.h"
2011-10-03 18:41:19 +00: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 21:47:03 +00:00
#include "packets/cPacket_NamedEntitySpawn.h"
2012-02-20 16:39:00 +00:00
#include "packets/cPacket_MapChunk.h"
2012-02-22 19:31:30 +00:00
#include "packets/cPacket_PreChunk.h"
2012-02-13 21:47:03 +00:00
2012-06-02 17:56:36 +00:00
// DEBUG:
#include "packets/cPacket_BlockChange.h"
2012-02-13 21:47:03 +00:00
2011-10-03 18:41:19 +00:00
2012-02-03 14:33:40 +00:00
#define AddPistonDir(x, y, z, dir, amount) switch (dir) { case 0: (y)-=(amount); break; case 1: (y)+=(amount); break;\
2011-11-07 22:59:29 +00:00
case 2: (z)-=(amount); break; case 3: (z)+=(amount); break;\
case 4: (x)-=(amount); break; case 5: (x)+=(amount); break; }
2012-02-26 00:36:51 +00:00
/// If the number of queued outgoing packets reaches this, the client will be kicked
#define MAX_OUTGOING_PACKETS 2000
2011-10-03 18:41:19 +00:00
2012-01-31 18:06:24 +00:00
2012-03-09 13:42:28 +00:00
int cClientHandle :: s_ClientCount = 0 ;
2012-01-31 18:06:24 +00:00
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cClientHandle:
2011-10-03 18:41:19 +00:00
2012-02-23 22:51:03 +00:00
cClientHandle :: cClientHandle ( const cSocket & a_Socket , int a_ViewDistance )
: m_ViewDistance ( a_ViewDistance )
2012-03-26 18:09:10 +00:00
, m_ProtocolVersion ( MCS_PROTOCOL_VERSION )
2012-02-03 14:33:40 +00:00
, m_Socket ( a_Socket )
, m_bDestroyed ( false )
2012-02-02 21:13:24 +00:00
, m_Player ( NULL )
2012-02-03 14:33:40 +00:00
, m_bKicking ( false )
, m_TimeLastPacket ( cWorld :: GetTime ())
, m_bKeepThreadGoing ( true )
2011-12-26 03:05:31 +00:00
, m_Ping ( 1000 )
2012-02-13 21:47:03 +00:00
, m_State ( csConnected )
, m_LastStreamedChunkX ( 0x7fffffff ) // bogus chunk coords to force streaming upon login
, m_LastStreamedChunkZ ( 0x7fffffff )
2012-03-09 13:42:28 +00:00
, m_UniqueID ( 0 )
2011-10-03 18:41:19 +00:00
{
2012-03-09 13:42:28 +00:00
s_ClientCount ++ ; // Not protected by CS because clients are always constructed from the same thread
m_UniqueID = s_ClientCount ;
2011-12-27 18:39:06 +00:00
cTimer t1 ;
m_LastPingTime = t1 . GetNowTime ();
2011-10-03 18:41:19 +00:00
// All the packets that can be received from the client
2012-02-03 14:33:40 +00: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 13:28:53 +00:00
m_PacketMap [ E_PLAYER_ABILITIES ] = new cPacket_PlayerAbilities ;
2012-02-03 14:33:40 +00: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 21:13:24 +00:00
2012-02-08 10:02:46 +00:00
LOG ( "New ClientHandle created at %p" , this );
2011-10-03 18:41:19 +00:00
}
2012-01-31 18:06:24 +00:00
2011-10-03 18:41:19 +00:00
cClientHandle ::~ cClientHandle ()
{
2012-02-26 00:36:51 +00:00
LOG ( "Deleting client \" %s \" at %p" , GetUsername (). c_str (), this );
2011-10-03 18:41:19 +00:00
2012-02-25 23:48:28 +00:00
// Remove from cSocketThreads, we're not to be called anymore:
2012-02-13 21:47:03 +00:00
cRoot :: Get () -> GetServer () -> ClientDestroying ( this );
2012-02-28 14:58:07 +00:00
{
cCSLock Lock ( m_CSChunkLists );
m_LoadedChunks . clear ();
m_ChunksToSend . clear ();
}
2011-10-03 18:41:19 +00:00
2012-02-13 21:47:03 +00:00
if ( m_Player != NULL )
2011-12-26 02:46:53 +00:00
{
2012-02-13 21:47:03 +00:00
cWorld * World = m_Player -> GetWorld ();
if ( ! m_Username . empty () && ( World != NULL ))
2011-12-26 02:46:53 +00:00
{
2012-02-13 21:47:03 +00:00
// Send the Offline PlayerList packet:
AString NameColor = ( m_Player ? m_Player -> GetColor () : "" );
2012-01-02 19:03:19 +00:00
cPacket_PlayerListItem PlayerList ( NameColor + GetUsername (), false , ( short ) 9999 );
2012-02-13 21:47:03 +00:00
World -> Broadcast ( PlayerList , this );
// Send the Chat packet:
cPacket_Chat Left ( m_Username + " left the game!" );
World -> Broadcast ( Left , this );
2011-12-26 02:46:53 +00:00
}
2012-02-26 12:55:42 +00:00
if ( World != NULL )
{
World -> RemovePlayer ( m_Player );
}
2011-12-26 03:05:31 +00:00
}
2011-12-26 02:46:53 +00:00
2012-02-03 14:33:40 +00:00
if ( m_Socket . IsValid ())
2011-10-03 18:41:19 +00:00
{
cPacket_Disconnect Disconnect ;
Disconnect . m_Reason = "Server shut down? Kthnxbai" ;
2012-02-07 20:49:52 +00:00
m_Socket . Send ( & Disconnect );
2011-10-03 18:41:19 +00:00
}
2012-02-26 00:36:51 +00:00
2012-02-02 21:13:24 +00:00
if ( m_Player != NULL )
2011-10-03 18:41:19 +00:00
{
2012-01-01 16:20:52 +00:00
m_Player -> Destroy ();
2012-02-02 21:13:24 +00:00
m_Player = NULL ;
2012-01-01 16:20:52 +00:00
}
2012-02-03 14:33:40 +00:00
for ( int i = 0 ; i < ARRAYCOUNT ( m_PacketMap ); i ++ )
2011-10-03 18:41:19 +00:00
{
2012-02-03 14:33:40 +00:00
delete m_PacketMap [ i ];
2011-10-03 18:41:19 +00:00
}
2012-02-13 21:47:03 +00:00
2012-02-26 00:36:51 +00:00
// Queue all remaining outgoing packets to cSocketThreads:
2012-02-13 21:47:03 +00:00
{
2012-02-26 00:36:51 +00:00
cCSLock Lock ( m_CSPackets );
2012-02-13 21:47:03 +00:00
for ( PacketList :: iterator itr = m_PendingNrmSendPackets . begin (); itr != m_PendingNrmSendPackets . end (); ++ itr )
{
2012-02-26 00:36:51 +00:00
AString Data ;
( * itr ) -> Serialize ( Data );
cRoot :: Get () -> GetServer () -> WriteToClient ( & m_Socket , Data );
2012-02-13 21:47:03 +00:00
delete * itr ;
}
2012-02-26 00:36:51 +00:00
m_PendingNrmSendPackets . clear ();
2012-02-13 21:47:03 +00:00
for ( PacketList :: iterator itr = m_PendingLowSendPackets . begin (); itr != m_PendingLowSendPackets . end (); ++ itr )
{
2012-02-26 00:36:51 +00:00
AString Data ;
( * itr ) -> Serialize ( Data );
cRoot :: Get () -> GetServer () -> WriteToClient ( & m_Socket , Data );
2012-02-13 21:47:03 +00:00
delete * itr ;
}
2012-02-26 00:36:51 +00:00
m_PendingLowSendPackets . clear ();
2012-02-13 21:47:03 +00:00
}
2012-02-08 10:02:46 +00:00
2012-02-26 00:36:51 +00:00
// Queue the socket to close as soon as it sends all outgoing data:
cRoot :: Get () -> GetServer () -> QueueClientClose ( & m_Socket );
2012-02-26 12:55:42 +00: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-26 00:36:51 +00:00
LOG ( "ClientHandle at %p deleted" , this );
2011-10-03 18:41:19 +00:00
}
2012-01-31 18:06:24 +00:00
2011-10-03 18:41:19 +00:00
void cClientHandle :: Destroy ()
{
2012-02-28 14:58:07 +00: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 21:47:03 +00:00
if (( m_Player != NULL ) && ( m_Player -> GetWorld () != NULL ))
{
RemoveFromAllChunks ();
2012-03-06 14:52:44 +00:00
m_Player -> GetWorld () -> RemoveClientFromChunkSender ( this );
2012-02-13 21:47:03 +00:00
}
2012-02-28 14:58:07 +00:00
m_bDestroyed = true ;
2011-10-03 18:41:19 +00:00
}
2012-01-31 18:06:24 +00:00
2012-02-01 22:38:03 +00:00
void cClientHandle :: Kick ( const AString & a_Reason )
2011-10-03 18:41:19 +00:00
{
2012-02-13 21:47:03 +00: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 22:38:03 +00:00
Send ( cPacket_Disconnect ( a_Reason ));
2011-10-03 18:41:19 +00:00
m_bKicking = true ;
}
2012-01-31 18:06:24 +00:00
2012-02-01 22:38:03 +00:00
void cClientHandle :: Authenticate ( void )
{
2012-02-29 20:25:11 +00:00
if ( m_State != csAuthenticating )
{
return ;
}
2012-02-29 13:40:40 +00:00
ASSERT ( m_Player == NULL );
2012-02-29 20:25:11 +00:00
2012-02-13 21:47:03 +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 ();
}
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 22:27:24 +00:00
LoginResponse . m_MaxPlayers = ( unsigned char ) cRoot :: Get () -> GetDefaultWorld () -> GetMaxPlayers ();
2012-02-13 21:47:03 +00: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-26 00:36:51 +00:00
m_State = csDownloadingWorld ;
2012-02-01 22:38:03 +00:00
}
void cClientHandle :: StreamChunks ( void )
2011-10-03 18:41:19 +00:00
{
2012-02-26 00:36:51 +00:00
if ( m_State < csAuthenticating )
2012-02-02 21:13:24 +00:00
{
2011-10-03 18:41:19 +00:00
return ;
2012-02-02 21:13:24 +00:00
}
2011-11-01 21:57:08 +00:00
2012-02-19 23:00:00 +00:00
ASSERT ( m_Player != NULL );
2012-02-02 21:13:24 +00:00
2012-03-14 20:56:09 +00:00
int ChunkPosX = FAST_FLOOR_DIV ( m_Player -> GetPosX (), cChunkDef :: Width );
int ChunkPosZ = FAST_FLOOR_DIV ( m_Player -> GetPosZ (), cChunkDef :: Width );
2012-02-13 21:47:03 +00:00
if (( ChunkPosX == m_LastStreamedChunkX ) && ( ChunkPosZ == m_LastStreamedChunkZ ))
{
// Already streamed for this position
return ;
}
m_LastStreamedChunkX = ChunkPosX ;
m_LastStreamedChunkZ = ChunkPosZ ;
2012-03-24 13:28:53 +00:00
LOGD ( "Streaming chunks centered on [%d, %d], view distance %d" , ChunkPosX , ChunkPosZ , m_ViewDistance );
2012-02-13 21:47:03 +00:00
2012-02-02 21:13:24 +00:00
cWorld * World = m_Player -> GetWorld ();
2012-02-19 23:00:00 +00:00
ASSERT ( World != NULL );
2011-12-24 23:34:30 +00:00
2012-03-06 14:52:44 +00:00
// Remove all loaded chunks that are no longer in range; deferred to out-of-CS:
cChunkCoordsList RemoveChunks ;
2011-10-03 18:41:19 +00:00
{
2012-02-13 21:47:03 +00:00
cCSLock Lock ( m_CSChunkLists );
for ( cChunkCoordsList :: iterator itr = m_LoadedChunks . begin (); itr != m_LoadedChunks . end ();)
2011-10-03 18:41:19 +00:00
{
2012-02-13 21:47:03 +00:00
int RelX = ( * itr ). m_ChunkX - ChunkPosX ;
int RelZ = ( * itr ). m_ChunkZ - ChunkPosZ ;
2012-02-23 22:51:03 +00:00
if (( RelX > m_ViewDistance ) || ( RelX < - m_ViewDistance ) || ( RelZ > m_ViewDistance ) || ( RelZ < - m_ViewDistance ))
2012-02-02 21:13:24 +00:00
{
2012-03-06 14:52:44 +00:00
RemoveChunks . push_back ( * itr );
2012-02-13 21:47:03 +00:00
itr = m_LoadedChunks . erase ( itr );
2012-02-02 21:13:24 +00:00
}
2012-02-13 21:47:03 +00:00
else
2011-10-03 18:41:19 +00:00
{
2012-02-13 21:47:03 +00:00
++ itr ;
2011-10-03 18:41:19 +00:00
}
2012-02-13 21:47:03 +00:00
} // for itr - m_LoadedChunks[]
for ( cChunkCoordsList :: iterator itr = m_ChunksToSend . begin (); itr != m_ChunksToSend . end ();)
2011-10-03 18:41:19 +00:00
{
2012-02-13 21:47:03 +00:00
int RelX = ( * itr ). m_ChunkX - ChunkPosX ;
int RelZ = ( * itr ). m_ChunkZ - ChunkPosZ ;
2012-02-23 22:51:03 +00:00
if (( RelX > m_ViewDistance ) || ( RelX < - m_ViewDistance ) || ( RelZ > m_ViewDistance ) || ( RelZ < - m_ViewDistance ))
2012-02-13 21:47:03 +00:00
{
itr = m_ChunksToSend . erase ( itr );
}
else
{
++ itr ;
}
2012-03-06 14:52:44 +00:00
} // for itr - m_ChunksToSend[]
2011-10-03 18:41:19 +00:00
}
2012-03-06 14:52:44 +00: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 21:47:03 +00: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 22:51:03 +00:00
for ( int d = 0 ; d <= m_ViewDistance ; ++ d ) // cycle through (square) distance, from nearest to furthest
2012-02-02 21:13:24 +00:00
{
2012-02-13 21:47:03 +00:00
// For each distance add chunks in a hollow square centered around current position:
for ( int i = - d ; i <= d ; ++ i )
2011-10-03 18:41:19 +00:00
{
2012-02-17 17:56:25 +00:00
StreamChunk ( ChunkPosX + d , ZERO_CHUNK_Y , ChunkPosZ + i );
StreamChunk ( ChunkPosX - d , ZERO_CHUNK_Y , ChunkPosZ + i );
2012-02-13 21:47:03 +00:00
} // for i
for ( int i = - d + 1 ; i < d ; ++ i )
{
2012-02-17 17:56:25 +00:00
StreamChunk ( ChunkPosX + i , ZERO_CHUNK_Y , ChunkPosZ + d );
StreamChunk ( ChunkPosX + i , ZERO_CHUNK_Y , ChunkPosZ - d );
2012-02-13 21:47:03 +00:00
} // for i
} // for d
// Touch chunks GENERATEDISTANCE ahead to let them generate:
2012-02-23 22:51:03 +00:00
for ( int d = m_ViewDistance + 1 ; d <= m_ViewDistance + GENERATEDISTANCE ; ++ d ) // cycle through (square) distance, from nearest to furthest
2012-02-13 21:47:03 +00:00
{
// For each distance touch chunks in a hollow square centered around current position:
for ( int i = - d ; i <= d ; ++ i )
{
2012-02-21 16:27:30 +00:00
World -> TouchChunk ( ChunkPosX + d , ZERO_CHUNK_Y , ChunkPosZ + i );
World -> TouchChunk ( ChunkPosX - d , ZERO_CHUNK_Y , ChunkPosZ + i );
2012-02-13 21:47:03 +00:00
} // for i
for ( int i = - d + 1 ; i < d ; ++ i )
{
2012-02-21 16:27:30 +00:00
World -> TouchChunk ( ChunkPosX + i , ZERO_CHUNK_Y , ChunkPosZ + d );
World -> TouchChunk ( ChunkPosX + i , ZERO_CHUNK_Y , ChunkPosZ - d );
2012-02-13 21:47:03 +00:00
} // for i
} // for d
}
2011-12-24 23:34:30 +00:00
2011-10-03 18:41:19 +00:00
2012-02-13 21:47:03 +00:00
2012-02-17 17:56:25 +00:00
void cClientHandle :: StreamChunk ( int a_ChunkX , int a_ChunkY , int a_ChunkZ )
2012-02-13 21:47:03 +00:00
{
cWorld * World = m_Player -> GetWorld ();
2012-02-19 23:00:00 +00:00
ASSERT ( World != NULL );
2012-02-13 21:47:03 +00:00
2012-02-21 15:18:02 +00:00
if ( World -> AddChunkClient ( a_ChunkX , a_ChunkY , a_ChunkZ , this ))
2012-02-13 21:47:03 +00:00
{
2012-05-25 07:18:52 +00:00
{
cCSLock Lock ( m_CSChunkLists );
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 14:52:44 +00:00
World -> SendChunkTo ( a_ChunkX , a_ChunkY , a_ChunkZ , this );
2011-10-03 18:41:19 +00:00
}
}
2012-01-31 18:06:24 +00:00
2012-02-13 21:47:03 +00:00
// Removes the client from all chunks. Used when switching worlds or destroying the player
void cClientHandle :: RemoveFromAllChunks ()
2011-10-03 18:41:19 +00:00
{
2012-02-13 21:47:03 +00:00
cWorld * World = m_Player -> GetWorld ();
if ( World != NULL )
2011-10-03 18:41:19 +00:00
{
2012-03-22 15:53:40 +00:00
World -> RemoveClientFromChunks ( this );
}
{
cCSLock Lock ( m_CSChunkLists );
m_LoadedChunks . clear ();
m_ChunksToSend . clear ();
2011-10-03 18:41:19 +00:00
}
}
2012-01-31 18:06:24 +00:00
2012-02-03 14:33:40 +00:00
void cClientHandle :: HandlePacket ( cPacket * a_Packet )
2011-10-03 18:41:19 +00:00
{
m_TimeLastPacket = cWorld :: GetTime ();
2012-02-13 21:47:03 +00: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 18:41:19 +00:00
2012-02-02 21:13:24 +00:00
if ( m_bKicking )
{
return ;
}
2011-10-03 18:41:19 +00:00
2012-02-13 21:47:03 +00:00
switch ( m_State )
2011-10-03 18:41:19 +00:00
{
2012-02-13 21:47:03 +00:00
case csConnected :
2011-10-03 18:41:19 +00:00
{
2012-02-13 21:47:03 +00:00
switch ( a_Packet -> m_PacketID )
2011-12-26 14:29:52 +00:00
{
2012-02-13 21:47:03 +00: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 23:48:28 +00:00
case E_CHAT :
2012-02-13 21:47:03 +00: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 23:48:28 +00:00
case E_CHAT :
case E_FLYING :
2012-02-13 21:47:03 +00:00
case E_PLAYERLOOK :
case E_PLAYERMOVELOOK :
case E_PLAYERPOS : break ;
default : HandleUnexpectedPacket ( a_Packet ); break ;
2011-12-26 14:29:52 +00:00
}
2012-02-13 21:47:03 +00:00
break ;
}
2012-01-01 04:55:17 +00:00
2012-02-13 21:47:03 +00:00
case csDownloadingWorld :
2012-01-19 18:12:39 +00:00
{
2012-02-13 21:47:03 +00: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 23:48:28 +00:00
case E_CHAT :
case E_FLYING :
2012-02-13 21:47:03 +00: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 23:48:28 +00:00
case E_CHAT :
case E_FLYING :
2012-02-13 21:47:03 +00:00
case E_PLAYERLOOK :
case E_PLAYERPOS : break ;
2012-01-19 18:12:39 +00:00
2012-02-13 21:47:03 +00: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 18:41:19 +00:00
{
2012-02-13 21:47:03 +00: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 14:33:40 +00:00
}
2012-02-02 21:13:24 +00:00
2011-11-10 16:30:14 +00:00
2011-12-28 21:00:35 +00:00
2011-10-03 18:41:19 +00:00
2012-02-03 14:33:40 +00: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 22:27:24 +00:00
cRoot :: Get () -> GetDefaultWorld () -> GetDescription (). c_str (),
2012-02-03 14:33:40 +00:00
cChatColor :: Delimiter . c_str (),
2012-03-10 22:27:24 +00:00
cRoot :: Get () -> GetDefaultWorld () -> GetNumPlayers (),
2012-02-03 14:33:40 +00:00
cChatColor :: Delimiter . c_str (),
2012-03-10 22:27:24 +00:00
cRoot :: Get () -> GetDefaultWorld () -> GetMaxPlayers ()
2012-02-03 14:33:40 +00:00
);
Kick ( Reply . c_str ());
}
2011-12-25 01:40:31 +00:00
2011-12-28 21:00:35 +00:00
2012-02-03 14:33:40 +00:00
void cClientHandle :: HandleHandshake ( cPacket_Handshake * a_Packet )
{
2012-03-01 17:07:41 +00: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 14:33:40 +00:00
LOG ( "HANDSHAKE %s" , m_Username . c_str ());
2011-11-10 16:30:14 +00:00
2012-03-10 22:27:24 +00:00
if ( cRoot :: Get () -> GetDefaultWorld () -> GetNumPlayers () >= cRoot :: Get () -> GetDefaultWorld () -> GetMaxPlayers ())
2012-02-03 14:33:40 +00: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 22:59:29 +00:00
2012-02-03 14:33:40 +00:00
cPacket_Handshake Handshake ;
Handshake . m_Username = cRoot :: Get () -> GetServer () -> GetServerID ();
Send ( Handshake );
2012-02-08 12:36:54 +00:00
LOG ( "User \" %s \" was sent a handshake" , m_Username . c_str ());
2012-02-03 14:33:40 +00:00
}
2011-10-03 18:41:19 +00:00
2011-11-11 00:21:52 +00:00
2012-02-03 14:33:40 +00: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 18:41:19 +00:00
2012-02-03 14:33:40 +00:00
if ( cRoot :: Get () -> GetPluginManager () -> CallHook ( cPluginManager :: E_PLUGIN_LOGIN , 1 , a_Packet ))
{
Destroy ();
return ;
}
2011-10-03 18:41:19 +00:00
2012-02-03 14:33:40 +00:00
// Schedule for authentication; until then, let them wait (but do not block)
2012-02-13 21:47:03 +00:00
m_State = csAuthenticating ;
2012-03-09 13:42:28 +00:00
cRoot :: Get () -> GetAuthenticator (). Authenticate ( GetUniqueID (), GetUsername (), cRoot :: Get () -> GetServer () -> GetServerID ());
2012-02-03 14:33:40 +00:00
}
2011-10-03 18:41:19 +00:00
2012-01-01 04:55:17 +00:00
2011-11-11 00:21:52 +00:00
2011-11-10 16:30:14 +00:00
2012-02-13 21:47:03 +00:00
void cClientHandle :: HandleUnexpectedPacket ( cPacket * a_Packet )
2012-02-03 14:33:40 +00:00
{
LOGWARNING (
2012-02-13 21:47:03 +00:00
"Invalid packet in state %d: 0x%02x from client \" %s \" , username \" %s \" " ,
m_State ,
2012-02-03 14:33:40 +00: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
}
2012-01-01 04:55:17 +00:00
2011-11-10 16:30:14 +00:00
2012-01-01 04:55:17 +00:00
2011-12-28 21:00:35 +00:00
2012-02-03 14:33:40 +00: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 21:47:03 +00:00
m_State = csPlaying ;
2012-02-03 14:33:40 +00:00
}
else
{
2012-02-13 21:47:03 +00: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 14:33:40 +00: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 14:07:18 +00: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 14:33:40 +00: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 13:54:33 +00:00
int pX = a_Packet -> m_PosX ;
unsigned char pY = a_Packet -> m_PosY ;
int pZ = a_Packet -> m_PosZ ;
2012-02-03 14:33:40 +00:00
2012-03-04 13:54:33 +00:00
AddDirection ( pX , pY , pZ , a_Packet -> m_Direction );
2012-02-03 14:33:40 +00:00
char PossibleBlock = World -> GetBlock ( pX , pY , pZ );
if ( PossibleBlock == E_BLOCK_FIRE )
{
a_Packet -> m_PosX = pX ;
2012-03-04 13:54:33 +00:00
a_Packet -> m_PosY = pY ;
2012-02-03 14:33:40 +00: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
{
2012-05-29 14:59:43 +00:00
LOGWARN ( "Player %s tried to place a block that was not equipped (exp %d, got %d)" ,
m_Username . c_str (), Equipped . m_ItemID , a_Packet -> m_ItemType
);
2012-02-03 14:33:40 +00:00
// 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 14:22:44 +00:00
case E_BLOCK_REDSTONE_REPEATER_ON :
case E_BLOCK_REDSTONE_REPEATER_OFF :
2012-02-03 14:33:40 +00:00
{
2012-02-15 14:22:44 +00: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 14:33:40 +00:00
}
2012-02-15 14:22:44 +00:00
case E_BLOCK_WORKBENCH :
2012-02-03 14:33:40 +00:00
{
bPlaceBlock = false ;
cWindow * Window = new cCraftingWindow ( 0 , true );
m_Player -> OpenWindow ( Window );
2012-02-15 14:22:44 +00:00
break ;
2012-02-03 14:33:40 +00:00
}
2012-02-15 14:22:44 +00:00
case E_BLOCK_FURNACE :
case E_BLOCK_CHEST :
2012-02-03 14:33:40 +00:00
{
bPlaceBlock = false ;
2012-02-15 14:22:44 +00:00
m_Player -> GetWorld () -> UseBlockEntity ( m_Player , a_Packet -> m_PosX , a_Packet -> m_PosY , a_Packet -> m_PosZ );
break ;
2012-02-03 14:33:40 +00:00
}
2012-02-15 14:22:44 +00:00
case E_BLOCK_WOODEN_DOOR :
2012-02-03 14:33:40 +00:00
{
bPlaceBlock = false ;
cDoors :: ChangeDoor ( m_Player -> GetWorld (), a_Packet -> m_PosX , a_Packet -> m_PosY , a_Packet -> m_PosZ );
2012-02-15 14:22:44 +00:00
break ;
2012-02-03 14:33:40 +00:00
}
2012-02-15 14:22:44 +00:00
default :
{
break ;
}
} // switch (BlockID)
} // if (Direction >= 0)
2012-02-03 14:33:40 +00: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 14:07:18 +00: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 14:33:40 +00:00
2012-05-29 19:56:07 +00:00
if ( m_Player -> EatItem ( Item . m_ItemID ))
2012-02-03 14:33:40 +00:00
{
m_Player -> GetInventory (). RemoveItem ( Item );
return ;
}
2012-05-29 20:45:25 +00:00
2012-02-03 14:33:40 +00:00
if ( a_Packet -> m_Direction < 0 )
{
// clicked in air
return ;
}
2012-05-29 19:56:07 +00:00
bool isDoor = false ;
2012-02-03 14:33:40 +00:00
//TODO: Wrong Blocks!
2012-05-29 20:45:25 +00:00
BLOCKTYPE ClickedBlock = m_Player -> GetWorld () -> GetBlock ( a_Packet -> m_PosX , a_Packet -> m_PosY , a_Packet -> m_PosZ );
if ( ItemCategory :: IsHoe ( Item . m_ItemID ))
{
if (( ClickedBlock == E_BLOCK_DIRT ) || ( ClickedBlock == E_BLOCK_GRASS ))
{
m_Player -> GetWorld () -> FastSetBlock ( a_Packet -> m_PosX , a_Packet -> m_PosY , a_Packet -> m_PosZ , E_BLOCK_FARMLAND , 0 );
}
return ;
}
2012-05-30 16:50:59 +00:00
NIBBLETYPE MetaData = ( NIBBLETYPE ) Equipped . m_ItemHealth ; // This generally works for logs & planks, others will override
2012-02-03 14:33:40 +00:00
bool LavaBucket = false ;
bool WaterBucket = false ;
bool bRemoveItem = true ;
bool bIgnoreCollision = false ;
if ( ClickedBlock == E_BLOCK_STEP )
{
2012-06-03 23:35:17 +00:00
// Only make double slab if meta values are the same and if player clicked either on top or on bottom of the block (direction either 0 or 1)
// TODO check if it works from beneath
if ( MetaData == ( m_Player -> GetWorld () -> GetBlockMeta ( a_Packet -> m_PosX , a_Packet -> m_PosY , a_Packet -> m_PosZ ) & 0x7 ) && a_Packet -> m_Direction <= 1 )
//if (MetaData == m_Player->GetWorld()->GetBlockMeta(a_Packet->m_PosX, a_Packet->m_PosY, a_Packet->m_PosZ) && a_Packet->m_Direction == 1)
2012-02-03 14:33:40 +00:00
{
2012-06-03 23:35:17 +00:00
a_Packet -> m_ItemType = E_BLOCK_DOUBLE_STEP ;
if ( a_Packet -> m_Direction == 1 )
{
a_Packet -> m_PosY -- ;
}
else
{
a_Packet -> m_PosY ++ ;
}
2012-02-03 14:33:40 +00:00
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 18:41:19 +00:00
}
}
2012-02-02 21:13:24 +00:00
break ;
2012-02-03 14:33:40 +00:00
} // case E_ITEM_BUCKET
2012-02-02 21:13:24 +00:00
2012-02-03 14:33:40 +00:00
case E_ITEM_LAVA_BUCKET :
2011-10-03 18:41:19 +00:00
{
2012-02-03 14:33:40 +00:00
if (( m_Player -> GetGameMode () == 1 ) || ( m_Player -> GetInventory (). RemoveItem ( Item )))
2011-10-03 18:41:19 +00:00
{
2012-02-03 14:33:40 +00: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 18:41:19 +00:00
}
2012-02-02 21:13:24 +00:00
break ;
2012-02-03 14:33:40 +00:00
} // case E_ITEM_LAVA_BUCKET
2012-02-02 21:13:24 +00:00
2012-02-03 14:33:40 +00:00
case E_ITEM_WATER_BUCKET :
2011-10-03 18:41:19 +00:00
{
2012-02-03 14:33:40 +00:00
if (( m_Player -> GetGameMode () == 1 ) || ( m_Player -> GetInventory (). RemoveItem ( Item )))
2011-10-03 18:41:19 +00:00
{
2012-02-03 14:33:40 +00: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 18:41:19 +00:00
}
2012-02-02 21:13:24 +00:00
break ;
2012-02-03 14:33:40 +00:00
}
2012-02-02 21:13:24 +00:00
2012-02-03 14:33:40 +00:00
case E_BLOCK_TORCH :
2011-10-03 18:41:19 +00:00
{
2012-02-03 14:33:40 +00:00
MetaData = cTorch :: DirectionToMetaData ( a_Packet -> m_Direction );
2012-02-02 21:13:24 +00:00
break ;
2012-02-03 14:33:40 +00:00
}
2012-02-02 21:13:24 +00:00
2012-02-03 14:33:40 +00:00
case E_BLOCK_REDSTONE_TORCH_OFF :
2011-10-03 18:41:19 +00:00
{
2012-02-03 14:33:40 +00: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 21:13:24 +00:00
break ;
2012-02-03 14:33:40 +00:00
}
2012-02-02 21:13:24 +00:00
2012-02-03 14:33:40 +00:00
case E_BLOCK_REDSTONE_TORCH_ON :
2011-10-03 18:41:19 +00:00
{
2012-02-03 14:33:40 +00:00
MetaData = cTorch :: DirectionToMetaData ( a_Packet -> m_Direction );
UpdateRedstone = true ;
AddedCurrent = true ;
2012-02-02 21:13:24 +00:00
break ;
2012-02-03 14:33:40 +00:00
}
2012-02-02 21:13:24 +00:00
2012-02-03 14:33:40 +00:00
case E_ITEM_REDSTONE_DUST :
2011-10-03 18:41:19 +00:00
{
2012-02-03 14:33:40 +00: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 18:41:19 +00:00
2012-02-03 14:33:40 +00: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 21:13:24 +00:00
break ;
2012-02-03 14:33:40 +00:00
}
2012-02-02 21:13:24 +00:00
2012-02-03 14:33:40 +00:00
case E_ITEM_WOODEN_DOOR :
2011-10-03 18:41:19 +00:00
{
2012-02-03 14:33:40 +00:00
a_Packet -> m_ItemType = E_BLOCK_WOODEN_DOOR ;
MetaData = cDoors :: RotationToMetaData ( m_Player -> GetRotation ());
isDoor = true ;
2012-02-02 21:13:24 +00:00
break ;
2012-02-03 14:33:40 +00:00
}
2012-02-02 21:13:24 +00:00
2012-02-03 14:33:40 +00:00
case E_BLOCK_CHEST :
case E_BLOCK_FURNACE :
case E_BLOCK_DISPENSER :
2011-10-03 18:41:19 +00:00
{
2012-02-03 14:33:40 +00:00
MetaData = cPiston :: RotationPitchToMetaData ( m_Player -> GetRotation (), 0 ); // Same orientation as pistons, just ignore pitch
break ;
}
2012-06-03 23:35:17 +00:00
case E_BLOCK_STEP :
{
MetaData += cStep :: DirectionToMetaData ( a_Packet -> m_Direction );
break ;
}
2012-02-03 14:33:40 +00:00
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 :
{
2012-06-03 23:35:17 +00:00
MetaData = cStairs :: RotationToMetaData ( m_Player -> GetRotation (), a_Packet -> m_Direction );
2012-02-03 14:33:40 +00:00
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 18:41:19 +00:00
{
2012-02-03 14:33:40 +00: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 18:41:19 +00:00
}
else
{
2012-02-03 14:33:40 +00:00
MetaData = cSign :: DirectionToMetaData ( a_Packet -> m_Direction );
a_Packet -> m_ItemType = E_BLOCK_WALLSIGN ;
2011-10-03 18:41:19 +00:00
}
2012-02-02 21:13:24 +00:00
break ;
2012-02-03 14:33:40 +00:00
}
case E_ITEM_FLINT_AND_STEEL :
2011-10-03 18:41:19 +00:00
{
2012-02-03 14:33:40 +00:00
a_Packet -> m_ItemType = E_ITEM_FIRE ;
m_Player -> UseEquippedItem ();
bRemoveItem = false ;
2012-02-02 21:13:24 +00:00
break ;
2012-02-03 14:33:40 +00:00
}
2012-05-30 16:50:59 +00:00
case E_ITEM_SEEDS :
{
if ( ClickedBlock != E_BLOCK_FARMLAND )
{
return ;
}
a_Packet -> m_ItemType = E_BLOCK_CROPS ;
break ;
}
case E_ITEM_MELON_SEEDS :
{
if ( ClickedBlock != E_BLOCK_FARMLAND )
{
return ;
}
a_Packet -> m_ItemType = E_BLOCK_MELON_STEM ;
break ;
}
case E_ITEM_PUMPKIN_SEEDS :
{
if ( ClickedBlock != E_BLOCK_FARMLAND )
{
return ;
}
a_Packet -> m_ItemType = E_BLOCK_PUMPKIN_STEM ;
break ;
}
2012-02-03 14:33:40 +00:00
default :
2011-10-03 18:41:19 +00:00
{
2012-02-03 14:33:40 +00: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 13:54:33 +00:00
int Y = a_Packet -> m_PosY ;
2012-02-03 14:33:40 +00: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
2012-05-29 14:59:43 +00:00
if ( ! m_Player -> GetWorld () -> IsPlacingItemLegal ( a_Packet -> m_ItemType , X , Y , Z ))
2011-10-03 18:41:19 +00:00
{
2012-02-03 14:33:40 +00:00
return ;
}
if ( bRemoveItem )
2011-10-03 18:41:19 +00:00
{
2012-02-03 14:33:40 +00:00
if (( m_Player -> GetGameMode () != 1 ) && ! m_Player -> GetInventory (). RemoveItem ( Item ))
2011-10-03 18:41:19 +00:00
{
2012-02-03 14:33:40 +00:00
return ;
2011-10-03 18:41:19 +00:00
}
2012-02-03 14:33:40 +00:00
}
if ( isDoor )
2011-12-27 18:39:06 +00:00
{
2012-02-03 14:33:40 +00: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 18:39:06 +00:00
{
2012-02-03 14:33:40 +00: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 18:39:06 +00:00
}
2012-02-03 14:33:40 +00: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 16:27:30 +00: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 14:33:40 +00:00
}
void cClientHandle :: HandleUseEntity ( cPacket_UseEntity * a_Packet )
{
if ( ! a_Packet -> m_bLeftClick )
{
return ;
}
2012-02-16 17:20:28 +00:00
class cDamageEntity : public cEntityCallback
2012-02-03 14:33:40 +00:00
{
2012-02-16 17:20:28 +00: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 14:33:40 +00: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 22:27:24 +00:00
ASSERT ( m_Player != NULL );
ASSERT ( m_Player -> GetWorld () != NULL );
2012-02-03 14:33:40 +00:00
int LastActionCnt = m_Player -> GetLastBlockActionCnt ();
2012-03-10 22:27:24 +00:00
if (( m_Player -> GetWorld () -> GetTime () - m_Player -> GetLastBlockActionTime ()) < 0.1 )
2012-02-03 14:33:40 +00: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 18:41:19 +00:00
}
2012-01-31 18:06:24 +00:00
2011-10-03 18:41:19 +00:00
void cClientHandle :: Tick ( float a_Dt )
{
( void ) a_Dt ;
2012-02-02 21:13:24 +00:00
if ( cWorld :: GetTime () - m_TimeLastPacket > 30.f ) // 30 seconds time-out
2011-10-03 18:41:19 +00:00
{
cPacket_Disconnect DC ( "Nooooo!! You timed out! D: Come back!" );
2012-02-07 20:49:52 +00:00
m_Socket . Send ( & DC );
2011-10-03 18:41:19 +00:00
2012-02-13 21:47:03 +00:00
// TODO: Cannot sleep in the tick thread!
2012-02-02 21:13:24 +00:00
cSleep :: MilliSleep ( 1000 ); // Give packet some time to be received
2011-10-03 18:41:19 +00:00
Destroy ();
}
2011-12-27 18:39:06 +00:00
cTimer t1 ;
// Send ping packet
2012-02-02 21:13:24 +00:00
if ( m_LastPingTime + cClientHandle :: PING_TIME_MS <= t1 . GetNowTime ())
{
2012-02-13 21:47:03 +00:00
m_PingID ++ ;
2011-12-27 18:39:06 +00:00
cPacket_KeepAlive Ping ( m_PingID );
m_PingStartTime = t1 . GetNowTime ();
Send ( Ping );
m_LastPingTime = m_PingStartTime ;
}
2011-10-03 18:41:19 +00:00
}
2012-01-31 18:06:24 +00:00
2012-03-10 21:34:47 +00:00
void cClientHandle :: Send ( const cPacket & a_Packet , ENUM_PRIORITY a_Priority /* = E_PRIORITY_NORMAL */ )
2011-10-03 18:41:19 +00:00
{
2012-02-03 14:33:40 +00:00
if ( m_bKicking ) return ; // Don't add more packets if player is getting kicked anyway
2011-10-03 18:41:19 +00:00
2012-02-13 21:47:03 +00:00
// If it is the packet spawning myself for myself, drop it silently:
2012-03-10 21:34:47 +00:00
if ( a_Packet . m_PacketID == E_NAMED_ENTITY_SPAWN )
2012-02-13 21:47:03 +00:00
{
2012-03-10 21:34:47 +00:00
if ((( cPacket_NamedEntitySpawn & ) a_Packet ). m_UniqueID == m_Player -> GetUniqueID ())
2012-02-13 21:47:03 +00:00
{
return ;
}
}
// Filter out packets that don't belong to a csDownloadingWorld state:
if ( m_State == csDownloadingWorld )
{
2012-03-10 21:34:47 +00:00
switch ( a_Packet . m_PacketID )
2012-02-13 21:47:03 +00:00
{
case E_PLAYERMOVELOOK :
case E_KEEP_ALIVE :
case E_PRE_CHUNK :
2012-03-05 16:41:57 +00:00
case E_MAP_CHUNK :
2012-02-13 21:47:03 +00:00
{
// Allow
break ;
}
2012-03-05 16:41:57 +00:00
default : return ;
}
}
2012-05-25 07:18:52 +00:00
// Filter out map chunks that the client doesn't want anymore:
2012-03-10 21:34:47 +00:00
if ( a_Packet . m_PacketID == E_MAP_CHUNK )
2012-03-05 16:41:57 +00:00
{
2012-04-10 11:22:11 +00:00
// Check chunks being sent, erase them from m_ChunksToSend:
2012-03-10 21:34:47 +00:00
int ChunkX = (( cPacket_MapChunk & ) a_Packet ). m_PosX ;
int ChunkZ = (( cPacket_MapChunk & ) a_Packet ). m_PosZ ;
2012-03-11 10:48:20 +00:00
bool Found = false ;
2012-03-05 16:41:57 +00: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 21:47:03 +00:00
{
2012-03-05 16:41:57 +00:00
m_ChunksToSend . erase ( itr );
2012-02-13 21:47:03 +00:00
CheckIfWorldDownloaded ();
2012-03-11 10:48:20 +00:00
Found = true ;
2012-02-13 21:47:03 +00:00
break ;
}
2012-03-05 16:41:57 +00:00
} // for itr - m_ChunksToSend[]
2012-03-11 10:48:20 +00:00
if ( ! Found )
{
2012-05-25 07:18:52 +00:00
// 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 ;
}
}
// Filter out pre chunks that the client doesn't want anymore:
if (( a_Packet . m_PacketID == E_PRE_CHUNK ) && (( cPacket_PreChunk & ) a_Packet ). m_bLoad )
{
int ChunkX = (( cPacket_PreChunk & ) a_Packet ). m_PosX ;
int ChunkZ = (( cPacket_PreChunk & ) a_Packet ). m_PosZ ;
bool Found = false ;
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 ))
{
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 PREchunk [%d, %d] to client \"%s\" at [%d, %d].", ChunkX, ChunkZ, m_Username.c_str(), m_Player->GetChunkX(), m_Player->GetChunkZ());
2012-03-11 10:48:20 +00:00
return ;
}
2012-02-13 21:47:03 +00:00
}
2012-03-05 16:41:57 +00:00
// Optimize away multiple queued RelativeEntityMoveLook packets:
2012-02-26 00:36:51 +00:00
cCSLock Lock ( m_CSPackets );
2012-02-02 21:13:24 +00:00
if ( a_Priority == E_PRIORITY_NORMAL )
2011-10-03 18:41:19 +00:00
{
2012-03-10 21:34:47 +00:00
if ( a_Packet . m_PacketID == E_REL_ENT_MOVE_LOOK )
2011-10-03 18:41:19 +00:00
{
2012-02-03 14:33:40 +00:00
PacketList & Packets = m_PendingNrmSendPackets ;
2012-03-10 21:34:47 +00:00
const cPacket_RelativeEntityMoveLook & ThisPacketData = reinterpret_cast < const cPacket_RelativeEntityMoveLook &> ( a_Packet );
2012-02-02 21:13:24 +00:00
for ( PacketList :: iterator itr = Packets . begin (); itr != Packets . end (); ++ itr )
2011-10-03 18:41:19 +00:00
{
bool bBreak = false ;
2012-02-02 21:13:24 +00:00
switch (( * itr ) -> m_PacketID )
2011-10-03 18:41:19 +00:00
{
2012-02-02 21:13:24 +00:00
case E_REL_ENT_MOVE_LOOK :
2011-10-03 18:41:19 +00:00
{
2012-03-10 21:34:47 +00:00
cPacket_RelativeEntityMoveLook * PacketData = reinterpret_cast < cPacket_RelativeEntityMoveLook *> ( * itr );
if ( ThisPacketData . m_UniqueID == PacketData -> m_UniqueID )
2011-10-03 18:41:19 +00:00
{
2012-02-03 14:33:40 +00:00
Packets . erase ( itr );
2011-10-03 18:41:19 +00:00
bBreak = true ;
2011-10-26 23:18:31 +00:00
delete PacketData ;
2011-10-03 18:41:19 +00:00
break ;
}
2012-02-02 21:13:24 +00:00
break ;
} // case E_REL_END_MOVE_LOOK
} // switch (*itr -> Packet type)
if ( bBreak )
{
2011-10-03 18:41:19 +00:00
break ;
}
2012-02-02 21:13:24 +00:00
} // for itr - Packets[]
} // if (E_REL_ENT_MOVE_LOOK
2012-03-10 21:34:47 +00:00
m_PendingNrmSendPackets . push_back ( a_Packet . Clone ());
2012-02-02 21:13:24 +00:00
}
2012-02-03 14:33:40 +00:00
else if ( a_Priority == E_PRIORITY_LOW )
2012-02-02 21:13:24 +00:00
{
2012-03-10 21:34:47 +00:00
m_PendingLowSendPackets . push_back ( a_Packet . Clone ());
2012-02-02 21:13:24 +00:00
}
Lock . Unlock ();
2012-02-26 00:36:51 +00:00
// Notify SocketThreads that we have something to write:
cRoot :: Get () -> GetServer () -> NotifyClientWrite ( this );
2011-10-03 18:41:19 +00:00
}
2012-01-31 18:06:24 +00:00
2012-02-13 21:47:03 +00:00
void cClientHandle :: CheckIfWorldDownloaded ( void )
{
if ( m_State != csDownloadingWorld )
{
return ;
}
cCSLock Lock ( m_CSChunkLists );
2012-03-02 14:09:52 +00:00
if ( m_ChunksToSend . empty ())
2012-02-13 21:47:03 +00:00
{
SendConfirmPosition ();
}
}
void cClientHandle :: SendConfirmPosition ( void )
{
2012-02-20 16:39:00 +00: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 21:47:03 +00: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 10:02:46 +00:00
const AString & cClientHandle :: GetUsername ( void ) const
2011-10-03 18:41:19 +00:00
{
2012-02-08 10:02:46 +00:00
return m_Username ;
}
2011-10-03 18:41:19 +00:00
2012-02-23 22:51:03 +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 ();
}
2012-03-11 10:48:20 +00: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-04-10 11:22:11 +00:00
void cClientHandle :: AddWantedChunk ( int a_ChunkX , int a_ChunkZ )
{
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-02-08 10:02:46 +00: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 18:41:19 +00:00
{
2012-02-08 10:02:46 +00: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 13:28:53 +00:00
Printf ( Reason , "[C->S] Unknown PacketID: 0x%02x" , ( unsigned char ) m_ReceivedData [ 0 ]);
2012-02-08 10:02:46 +00: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 18:41:19 +00:00
{
2012-02-08 10:02:46 +00: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 18:41:19 +00:00
break ;
}
2012-02-08 10:02:46 +00:00
else
2011-10-03 18:41:19 +00:00
{
2012-02-08 10:02:46 +00:00
// Packet parsed successfully, add it to internal queue:
HandlePacket ( pPacket );
// Erase the packet from the buffer:
2012-02-19 23:00:00 +00:00
ASSERT ( m_ReceivedData . size () > ( size_t ) NumBytes );
2012-02-08 10:02:46 +00:00
m_ReceivedData . erase ( 0 , NumBytes + 1 );
}
} // while (!Received.empty())
2011-10-03 18:41:19 +00:00
}
2012-01-31 18:06:24 +00:00
2012-02-08 10:02:46 +00:00
void cClientHandle :: GetOutgoingData ( AString & a_Data )
2011-10-03 18:41:19 +00:00
{
2012-02-08 10:02:46 +00:00
// Data can be sent to client
2012-02-26 00:36:51 +00: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 );
2012-06-02 17:56:36 +00:00
// DEBUG: Dump all outstanding packets' types to the log:
int Idx = 0 ;
int ChunkX = m_Player -> GetChunkX ();
int ChunkZ = m_Player -> GetChunkZ ();
for ( PacketList :: const_iterator itr = m_PendingNrmSendPackets . begin (); itr != m_PendingNrmSendPackets . end (); ++ itr )
{
switch (( * itr ) -> m_PacketID )
{
case E_MAP_CHUNK :
{
int x = (( cPacket_MapChunk * )( * itr )) -> m_PosX ;
int z = (( cPacket_MapChunk * )( * itr )) -> m_PosZ ;
bool IsWanted = ( abs ( x - ChunkX ) <= m_ViewDistance ) && ( abs ( z - ChunkZ ) <= m_ViewDistance );
LOG ( "Packet %4d: type %2x (MapChunk: %d, %d, %s)" ,
Idx ++ , ( * itr ) -> m_PacketID ,
x , z ,
IsWanted ? "wanted" : "unwanted"
);
break ;
}
case E_PRE_CHUNK :
{
int x = (( cPacket_PreChunk * )( * itr )) -> m_PosX ;
int z = (( cPacket_PreChunk * )( * itr )) -> m_PosZ ;
bool IsWanted = ( abs ( x - ChunkX ) <= m_ViewDistance ) && ( abs ( z - ChunkZ ) <= m_ViewDistance );
bool Loading = (( cPacket_PreChunk * )( * itr )) -> m_bLoad ;
LOG ( "Packet %4d: type %2x (PreChunk: %d, %d, %s, %s)" ,
Idx ++ , ( * itr ) -> m_PacketID ,
x , z ,
Loading ? "loading" : "unloading" ,
IsWanted ? "wanted" : "unwanted"
);
break ;
}
case E_BLOCK_CHANGE :
{
int x = (( cPacket_BlockChange * )( * itr )) -> m_PosX ;
int z = (( cPacket_BlockChange * )( * itr )) -> m_PosZ ;
int y , cx , cz ;
cChunkDef :: AbsoluteToRelative ( x , y , z , cx , cz );
bool IsWanted = ( abs ( cx - ChunkX ) <= m_ViewDistance ) && ( abs ( cz - ChunkZ ) <= m_ViewDistance );
LOG ( "Packet %4d: type %2x (BlockChange: [%d, %d], %s chunk)" ,
Idx ++ , ( * itr ) -> m_PacketID ,
cx , cz ,
IsWanted ? "wanted" : "unwanted"
);
break ;
}
default :
{
LOG ( "Packet %4d: type %2x" , Idx ++ , ( * itr ) -> m_PacketID );
break ;
}
}
}
2012-02-26 00:36:51 +00:00
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 18:41:19 +00:00
}
2012-01-31 18:06:24 +00:00
2012-02-08 10:02:46 +00:00
void cClientHandle :: SocketClosed ( void )
2011-10-03 18:41:19 +00:00
{
2012-02-08 10:02:46 +00:00
// The socket has been closed for any reason
// TODO
/*
self->Destroy();
LOG("Client \"%s\" disconnected", GetLogName().c_str());
*/
2011-10-26 03:22:43 +00:00
}
2012-01-31 18:06:24 +00:00