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 "cPlayer.h"
#include "cServer.h"
2012-01-01 04:55:17 +00:00
#include "cCreativeInventory.h"
#include "cSurvivalInventory.h"
2011-10-03 18:41:19 +00:00
#include "cClientHandle.h"
#include "cWorld.h"
#include "cPickup.h"
#include "cPluginManager.h"
#include "cChunk.h"
#include "cWindow.h"
#include "cBlockEntity.h"
#include "cGroupManager.h"
#include "cGroup.h"
#include "cChatColor.h"
#include "cItem.h"
#include "cTracer.h"
#include "cRoot.h"
2011-11-01 21:57:08 +00:00
#include "cMakeDir.h"
2011-12-26 04:06:29 +00:00
#include "cTimer.h"
2011-12-26 09:09:47 +00:00
#include "MersenneTwister.h"
2011-10-03 18:41:19 +00:00
#include "packets/cPacket_NamedEntitySpawn.h"
#include "packets/cPacket_EntityLook.h"
#include "packets/cPacket_TeleportEntity.h"
#include "packets/cPacket_RelativeEntityMove.h"
#include "packets/cPacket_RelativeEntityMoveLook.h"
#include "packets/cPacket_UpdateHealth.h"
#include "packets/cPacket_Respawn.h"
#include "packets/cPacket_PlayerPosition.h"
#include "packets/cPacket_DestroyEntity.h"
#include "packets/cPacket_Metadata.h"
#include "packets/cPacket_Chat.h"
2011-11-10 18:28:21 +00:00
#include "packets/cPacket_NewInvalidState.h"
2011-12-26 01:07:35 +00:00
#include "packets/cPacket_PlayerListItem.h"
2011-12-29 13:16:23 +00:00
#include "packets/cPacket_BlockAction.h"
2011-10-03 18:41:19 +00:00
#include "Vector3d.h"
#include "Vector3f.h"
#include "../iniFile/iniFile.h"
2011-10-31 21:30:14 +00:00
#include <json/json.h>
2011-10-03 18:41:19 +00:00
2011-11-06 09:23:20 +00:00
#define float2int(x) ((x)<0 ? ((int)(x))-1 : (int)(x))
2012-02-01 09:12:54 +00:00
2011-10-03 18:41:19 +00:00
CLASS_DEFINITION ( cPlayer , cPawn );
typedef std :: map < std :: string , bool > PermissionMap ;
struct cPlayer :: sPlayerState
{
PermissionMap ResolvedPermissions ;
PermissionMap Permissions ;
cPlayer :: GroupList ResolvedGroups ;
cPlayer :: GroupList Groups ;
std :: string PlayerName ;
2011-11-02 20:19:57 +00:00
std :: string LoadedWorldName ;
2011-10-03 18:41:19 +00:00
};
2012-02-01 22:38:03 +00:00
cPlayer :: cPlayer ( cClientHandle * a_Client , const AString & a_PlayerName )
2011-12-28 02:10:05 +00:00
: m_GameMode ( 0 )
2011-11-09 22:17:30 +00:00
, m_IP ( "" )
2011-11-01 20:09:13 +00:00
, m_LastBlockActionTime ( 0 )
2011-11-10 16:30:14 +00:00
, m_LastBlockActionCnt ( 0 )
2011-10-03 18:41:19 +00:00
, m_bVisible ( true )
, m_LastGroundHeight ( 0 )
, m_bTouchGround ( false )
, m_Stance ( 0.0 )
, m_Inventory ( 0 )
, m_CurrentWindow ( 0 )
, m_TimeLastPickupCheck ( 0.f )
, m_Color ( '-' )
, m_ClientHandle ( a_Client )
, m_pState ( new sPlayerState )
{
m_EntityType = E_PLAYER ;
2011-12-28 02:10:05 +00:00
SetMaxHealth ( 20 );
2011-12-29 15:31:48 +00:00
SetMaxFoodLevel ( 125 );
2012-01-01 04:55:17 +00:00
m_Inventory = new cSurvivalInventory ( this );
m_CreativeInventory = new cCreativeInventory ( this );
2011-12-26 04:06:29 +00:00
cTimer t1 ;
m_LastPlayerListTime = t1 . GetNowTime ();
2011-10-03 18:41:19 +00:00
m_TimeLastTeleportPacket = cWorld :: GetTime ();
m_TimeLastPickupCheck = cWorld :: GetTime ();
2011-11-06 09:23:20 +00:00
2011-10-03 18:41:19 +00:00
m_pState -> PlayerName = a_PlayerName ;
m_bDirtyPosition = true ; // So chunks are streamed to player at spawn
if ( ! LoadFromDisk () )
{
m_Inventory -> Clear ();
2012-01-01 18:45:28 +00:00
m_CreativeInventory -> Clear ();
2011-11-01 21:57:08 +00:00
SetPosX ( cRoot :: Get () -> GetDefaultWorld () -> GetSpawnX () );
SetPosY ( cRoot :: Get () -> GetDefaultWorld () -> GetSpawnY () );
SetPosZ ( cRoot :: Get () -> GetDefaultWorld () -> GetSpawnZ () );
2011-10-03 18:41:19 +00:00
}
2011-11-01 21:57:08 +00:00
}
2011-10-03 18:41:19 +00:00
2011-11-01 21:57:08 +00:00
void cPlayer :: Initialize ( cWorld * a_World )
{
cPawn :: Initialize ( a_World );
GetWorld () -> AddPlayer ( this );
2011-10-03 18:41:19 +00:00
}
cPlayer ::~ cPlayer ( void )
{
SaveToDisk ();
m_ClientHandle = 0 ;
2011-11-06 09:23:20 +00:00
2011-12-25 14:03:01 +00:00
CloseWindow ( - 1 );
2011-10-03 18:41:19 +00:00
if ( m_Inventory )
{
delete m_Inventory ;
m_Inventory = 0 ;
}
2012-01-01 04:55:17 +00:00
if ( m_CreativeInventory )
{
delete m_CreativeInventory ;
}
2011-10-03 18:41:19 +00:00
delete m_pState ;
2011-11-02 20:19:57 +00:00
GetWorld () -> RemovePlayer ( this ); // TODO - Remove from correct world? Or get rid of this?
2011-10-03 18:41:19 +00:00
}
2011-11-01 20:09:13 +00:00
2011-10-03 18:41:19 +00:00
void cPlayer :: SpawnOn ( cClientHandle * a_Target )
{
if ( a_Target == m_ClientHandle || ! m_bVisible ) return ;
2012-02-01 22:38:03 +00:00
LOG ( "cPlayer::SpawnOn -> Sending %s to %s" , m_pState -> PlayerName . c_str (), ( a_Target ) ? a_Target -> GetUsername (). c_str () : "Everybody" );
2011-10-03 18:41:19 +00:00
cPacket_NamedEntitySpawn SpawnPacket ;
SpawnPacket . m_UniqueID = m_UniqueID ;
SpawnPacket . m_PlayerName = m_pState -> PlayerName ;
SpawnPacket . m_PosX = ( int )( m_Pos -> x * 32 );
SpawnPacket . m_PosY = ( int )( m_Pos -> y * 32 );
SpawnPacket . m_PosZ = ( int )( m_Pos -> z * 32 );
SpawnPacket . m_Rotation = ( char )(( m_Rot -> x / 360.f ) * 256 );
SpawnPacket . m_Pitch = ( char )(( m_Rot -> y / 360.f ) * 256 );
SpawnPacket . m_CurrentItem = ( short ) m_Inventory -> GetEquippedItem (). m_ItemID ;
if ( a_Target == 0 )
{
2011-10-31 00:52:20 +00:00
cChunk * Chunk = GetWorld () -> GetChunk ( m_ChunkX , m_ChunkY , m_ChunkZ );
2011-10-03 18:41:19 +00:00
Chunk -> Broadcast ( SpawnPacket , m_ClientHandle );
}
else
{
a_Target -> Send ( SpawnPacket );
}
}
void cPlayer :: Tick ( float a_Dt )
{
2011-10-31 00:52:20 +00:00
cChunk * InChunk = GetWorld () -> GetChunk ( m_ChunkX , m_ChunkY , m_ChunkZ );
2012-02-02 21:13:24 +00:00
if ( ! InChunk ) return ;
2011-12-24 23:34:30 +00:00
2011-12-28 02:10:05 +00:00
cPawn :: Tick ( a_Dt );
2012-02-02 21:13:24 +00:00
if ( m_bDirtyOrientation && ! m_bDirtyPosition )
2011-10-03 18:41:19 +00:00
{
cPacket_EntityLook EntityLook ( this );
InChunk -> Broadcast ( EntityLook , m_ClientHandle );
m_bDirtyOrientation = false ;
2012-02-02 21:13:24 +00:00
}
else if ( m_bDirtyPosition )
2011-10-03 18:41:19 +00:00
{
cRoot :: Get () -> GetPluginManager () -> CallHook ( cPluginManager :: E_PLUGIN_PLAYER_MOVE , 1 , this );
float DiffX = ( float )( GetPosX () - m_LastPosX );
float DiffY = ( float )( GetPosY () - m_LastPosY );
float DiffZ = ( float )( GetPosZ () - m_LastPosZ );
float SqrDist = DiffX * DiffX + DiffY * DiffY + DiffZ * DiffZ ;
if ( SqrDist > 4 * 4 // 4 blocks is max Relative Move
|| cWorld :: GetTime () - m_TimeLastTeleportPacket > 2 ) // Send an absolute position every 2 seconds
{
//LOG("Teleported %f", sqrtf(SqrDist) );
cPacket_TeleportEntity TeleportEntity ( this );
InChunk -> Broadcast ( TeleportEntity , m_ClientHandle );
m_TimeLastTeleportPacket = cWorld :: GetTime ();
}
else
{ // Relative move sucks balls! It's always wrong wtf!
if ( m_bDirtyOrientation )
{
cPacket_RelativeEntityMoveLook RelativeEntityMoveLook ;
RelativeEntityMoveLook . m_UniqueID = GetUniqueID ();
RelativeEntityMoveLook . m_MoveX = ( char )( DiffX * 32 );
RelativeEntityMoveLook . m_MoveY = ( char )( DiffY * 32 );
RelativeEntityMoveLook . m_MoveZ = ( char )( DiffZ * 32 );
RelativeEntityMoveLook . m_Yaw = ( char )(( GetRotation () / 360.f ) * 256 );
RelativeEntityMoveLook . m_Pitch = ( char )(( GetPitch () / 360.f ) * 256 );
InChunk -> Broadcast ( RelativeEntityMoveLook , m_ClientHandle );
}
else
{
cPacket_RelativeEntityMove RelativeEntityMove ;
RelativeEntityMove . m_UniqueID = GetUniqueID ();
RelativeEntityMove . m_MoveX = ( char )( DiffX * 32 );
RelativeEntityMove . m_MoveY = ( char )( DiffY * 32 );
RelativeEntityMove . m_MoveZ = ( char )( DiffZ * 32 );
InChunk -> Broadcast ( RelativeEntityMove , m_ClientHandle );
}
}
m_LastPosX = GetPosX ();
m_LastPosY = GetPosY ();
m_LastPosZ = GetPosZ ();
m_bDirtyPosition = false ;
m_ClientHandle -> StreamChunks ();
}
if ( m_Health > 0 ) // make sure player is alive
{
if ( cWorld :: GetTime () - m_TimeLastPickupCheck > 0.5f ) // Each 0.5 second, check for pickups
{
m_TimeLastPickupCheck = cWorld :: GetTime ();
// and also check if near a pickup
// TODO: Don't only check in current chunks, but also close chunks (chunks within range)
2011-10-31 00:52:20 +00:00
cChunk * Chunk = GetWorld () -> GetChunk ( m_ChunkX , m_ChunkY , m_ChunkZ );
2011-10-03 18:41:19 +00:00
Chunk -> LockEntities ();
cWorld :: EntityList Entities = Chunk -> GetEntities ();
for ( cWorld :: EntityList :: iterator itr = Entities . begin (); itr != Entities . end (); ++ itr )
{
if ( ( * itr ) -> GetEntityType () != cEntity :: E_PICKUP ) continue ; // Only pickups
float DiffX = ( float )(( * itr ) -> GetPosX () - GetPosX () );
float DiffY = ( float )(( * itr ) -> GetPosY () - GetPosY () );
float DiffZ = ( float )(( * itr ) -> GetPosZ () - GetPosZ () );
float SqrDist = DiffX * DiffX + DiffY * DiffY + DiffZ * DiffZ ;
if ( SqrDist < 1.5f * 1.5f ) // 1.5 block
{
cPickup * Pickup = reinterpret_cast < cPickup *> ( * itr );
Pickup -> CollectedBy ( this );
}
}
Chunk -> UnlockEntities ();
}
}
2011-12-26 01:07:35 +00:00
2011-12-26 04:06:29 +00:00
cTimer t1 ;
2011-12-27 18:39:06 +00:00
// Send Player List (Once per m_LastPlayerListTime/1000 ms)
2012-02-02 21:13:24 +00:00
if ( m_LastPlayerListTime + cPlayer :: PLAYER_LIST_TIME_MS <= t1 . GetNowTime ())
{
2011-12-26 04:06:29 +00:00
cWorld :: PlayerList PlayerList = cRoot :: Get () -> GetWorld () -> GetAllPlayers ();
2012-02-02 21:13:24 +00:00
for ( cWorld :: PlayerList :: iterator itr = PlayerList . begin (); itr != PlayerList . end (); ++ itr )
2011-12-26 04:06:29 +00:00
{
2012-02-02 21:13:24 +00:00
if (( * itr ) && ( * itr ) -> GetClientHandle () && ! (( * itr ) -> GetClientHandle () -> IsDestroyed ()))
{
cPacket_PlayerListItem PlayerListItem ( GetColor () + m_pState -> PlayerName , true , GetClientHandle () -> GetPing ());
2011-12-27 18:39:06 +00:00
( * itr ) -> GetClientHandle () -> Send ( PlayerListItem );
2011-12-26 04:06:29 +00:00
}
2011-12-26 01:07:35 +00:00
}
2011-12-26 04:06:29 +00:00
m_LastPlayerListTime = t1 . GetNowTime ();
2011-12-26 01:07:35 +00:00
}
2011-10-03 18:41:19 +00:00
}
void cPlayer :: SetTouchGround ( bool a_bTouchGround )
{
m_bTouchGround = a_bTouchGround ;
if ( ! m_bTouchGround )
{
2011-10-31 00:52:20 +00:00
cWorld * World = GetWorld ();
2011-11-06 09:23:20 +00:00
char BlockID = World -> GetBlock ( float2int ( m_Pos -> x ), float2int ( m_Pos -> y ), float2int ( m_Pos -> z ) );
2011-10-03 18:41:19 +00:00
if ( BlockID != E_BLOCK_AIR )
{
m_bTouchGround = true ;
}
if ( BlockID == E_BLOCK_WATER || BlockID == E_BLOCK_STATIONARY_WATER || BlockID == E_BLOCK_LADDER || BlockID == E_BLOCK_TORCH )
{
m_LastGroundHeight = ( float ) m_Pos -> y ;
}
}
if ( m_bTouchGround )
{
float Dist = ( float )( m_LastGroundHeight - m_Pos -> y );
if ( Dist > 4.f ) // Player dropped
{
int Damage = ( int )( Dist - 4.f );
if ( Damage > 0 )
{
TakeDamage ( Damage , 0 );
}
}
m_LastGroundHeight = ( float ) m_Pos -> y ;
}
}
void cPlayer :: Heal ( int a_Health )
{
2011-12-28 02:10:05 +00:00
if ( m_Health < GetMaxHealth () )
2011-10-03 18:41:19 +00:00
{
2011-12-28 02:10:05 +00:00
m_Health = ( short ) MIN ( a_Health + m_Health , GetMaxHealth ());
2011-10-03 18:41:19 +00:00
cPacket_UpdateHealth Health ;
Health . m_Health = m_Health ;
2011-12-29 15:31:48 +00:00
Health . m_Food = GetFood ();
Health . m_Saturation = GetFoodSaturation ();
m_ClientHandle -> Send ( Health );
}
}
void cPlayer :: Feed ( short a_Food )
{
if ( m_FoodLevel < GetMaxFoodLevel () )
{
m_FoodLevel = MIN ( a_Food + m_FoodLevel , GetMaxFoodLevel ());
cPacket_UpdateHealth Health ;
Health . m_Health = m_Health ;
Health . m_Food = GetFood ();
Health . m_Saturation = GetFoodSaturation ();
2011-10-03 18:41:19 +00:00
m_ClientHandle -> Send ( Health );
}
}
void cPlayer :: TakeDamage ( int a_Damage , cEntity * a_Instigator )
{
2011-11-01 20:26:11 +00:00
if ( ! ( m_GameMode == 1 ) ) {
2011-10-26 19:13:49 +00:00
cPawn :: TakeDamage ( a_Damage , a_Instigator );
2011-10-03 18:41:19 +00:00
2011-10-26 19:13:49 +00:00
cPacket_UpdateHealth Health ;
Health . m_Health = m_Health ;
2011-12-29 15:31:48 +00:00
Health . m_Food = GetFood ();
Health . m_Saturation = GetFoodSaturation ();
2011-12-21 20:42:34 +00:00
//TODO: Causes problems sometimes O.o (E.G. Disconnecting when attacked)
if ( m_ClientHandle != 0 )
m_ClientHandle -> Send ( Health );
2011-10-26 19:13:49 +00:00
}
2011-10-03 18:41:19 +00:00
}
void cPlayer :: KilledBy ( cEntity * a_Killer )
{
cPawn :: KilledBy ( a_Killer );
if ( m_Health > 0 ) return ; // not dead yet =]
m_bVisible = false ; // So new clients don't see the player
2011-12-26 09:09:47 +00:00
MTRand r1 ;
2011-10-03 18:41:19 +00:00
// Puke out all the items
cItem * Items = m_Inventory -> GetSlots ();
for ( unsigned int i = 1 ; i < m_Inventory -> c_NumSlots ; ++ i )
{
if ( ! Items [ i ]. IsEmpty () )
{
2011-12-26 09:09:47 +00:00
float SpeedX = (( r1 . randInt () % 1000 ) - 500 ) / 100.f ;
float SpeedY = (( r1 . randInt () % 1000 )) / 100.f ;
float SpeedZ = (( r1 . randInt () % 1000 ) - 500 ) / 100.f ;
2011-10-03 18:41:19 +00:00
cPickup * Pickup = new cPickup ( ( int )( m_Pos -> x * 32 ), ( int )( m_Pos -> y * 32 ), ( int )( m_Pos -> z * 32 ), Items [ i ], SpeedX , SpeedY , SpeedZ );
2011-10-31 00:52:20 +00:00
Pickup -> Initialize ( GetWorld () );
2011-10-03 18:41:19 +00:00
}
Items [ i ]. Empty ();
}
SaveToDisk (); // Save it, yeah the world is a tough place !
}
void cPlayer :: Respawn ()
{
2011-12-28 02:10:05 +00:00
m_Health = GetMaxHealth ();
2011-10-03 18:41:19 +00:00
2011-10-26 20:52:19 +00:00
// Create Respawn player packet
cPacket_Respawn Packet ;
//Set Gamemode for packet by looking at world's gamemode (Need to check players gamemode.)
2011-11-01 20:09:13 +00:00
//Packet.m_CreativeMode = (char)GetWorld()->GetGameMode();
Packet . m_CreativeMode = ( char ) m_GameMode ; //Set GameMode packet based on Player's GameMode;
2011-12-28 02:10:05 +00:00
//TODO Less hardcoded
Packet . m_World = 0 ;
Packet . m_MapSeed = GetWorld () -> GetWorldSeed ();
2011-10-26 20:52:19 +00:00
//Send Packet
m_ClientHandle -> Send ( Packet );
2011-12-28 02:10:05 +00:00
//Set non Burning
SetMetaData ( NORMAL );
2011-10-31 00:52:20 +00:00
TeleportTo ( GetWorld () -> GetSpawnX (), GetWorld () -> GetSpawnY (), GetWorld () -> GetSpawnZ () );
2011-12-28 02:10:05 +00:00
2011-10-03 18:41:19 +00:00
SetVisible ( true );
}
double cPlayer :: GetEyeHeight ()
{
return m_Stance ;
}
Vector3d cPlayer :: GetEyePosition ()
{
return Vector3d ( m_Pos -> x , m_Stance , m_Pos -> z );
}
void cPlayer :: OpenWindow ( cWindow * a_Window )
{
2011-12-25 14:03:01 +00:00
CloseWindow ( m_CurrentWindow ? ( char ) m_CurrentWindow -> GetWindowType () : 0 );
2011-10-03 18:41:19 +00:00
a_Window -> Open ( * this );
m_CurrentWindow = a_Window ;
}
2011-12-26 01:07:35 +00:00
void cPlayer :: CloseWindow ( char a_WindowType )
2011-10-03 18:41:19 +00:00
{
2011-12-29 13:16:23 +00:00
if ( a_WindowType == 0 ) { // Inventory
2012-01-01 04:55:17 +00:00
if ( m_Inventory -> GetWindow () -> GetDraggingItem () && m_Inventory -> GetWindow () -> GetDraggingItem () -> m_ItemCount > 0 )
2011-12-25 14:03:01 +00:00
{
LOG ( "Player holds item! Dropping it..." );
2012-01-01 04:55:17 +00:00
TossItem ( true , m_Inventory -> GetWindow () -> GetDraggingItem () -> m_ItemCount );
2011-12-25 14:03:01 +00:00
}
//Drop whats in the crafting slots (1, 2, 3, 4)
for ( int i = 1 ; i <= 4 ; i ++ )
{
cItem * Item = m_Inventory -> GetSlot ( i );
if ( Item -> m_ItemID > 0 && Item -> m_ItemCount > 0 )
{
float vX = 0 , vY = 0 , vZ = 0 ;
EulerToVector ( - GetRotation (), GetPitch (), vZ , vX , vY );
vY = - vY * 2 + 1.f ;
cPickup * Pickup = new cPickup ( ( int )( GetPosX () * 32 ), ( int )( GetPosY () * 32 ) + ( int )( 1.6f * 32 ), ( int )( GetPosZ () * 32 ), * Item , vX * 2 , vY * 2 , vZ * 2 );
Pickup -> Initialize ( GetWorld () );
}
Item -> Empty ();
}
}
2012-01-01 18:45:28 +00:00
if ( m_CurrentWindow )
{
if ( a_WindowType == 1 && strcmp ( m_CurrentWindow -> GetWindowTitle (). c_str (), "UberChest" ) == 0 ) { // Chest
cBlockEntity * block = m_CurrentWindow -> GetOwner () -> GetEntity ();
cPacket_BlockAction ChestClose ;
ChestClose . m_PosX = block -> GetPosX ();
ChestClose . m_PosY = ( short ) block -> GetPosY ();
ChestClose . m_PosZ = block -> GetPosZ ();
ChestClose . m_Byte1 = 1 ;
ChestClose . m_Byte2 = 0 ;
cWorld :: PlayerList PlayerList = cRoot :: Get () -> GetWorld () -> GetAllPlayers ();
for ( cWorld :: PlayerList :: iterator itr = PlayerList . begin (); itr != PlayerList . end (); ++ itr )
{
if (( * itr ) && ( * itr ) -> GetClientHandle () && ! (( * itr ) -> GetClientHandle () -> IsDestroyed ())) {
( * itr ) -> GetClientHandle () -> Send ( ChestClose );
}
2011-12-29 13:16:23 +00:00
}
}
2012-01-01 18:45:28 +00:00
m_CurrentWindow -> Close ( * this );
2011-12-29 13:16:23 +00:00
}
2011-10-03 18:41:19 +00:00
m_CurrentWindow = 0 ;
}
2011-11-01 20:09:13 +00:00
void cPlayer :: SetLastBlockActionTime ()
{
m_LastBlockActionTime = cRoot :: Get () -> GetWorld () -> GetTime ();
}
2011-11-10 16:30:14 +00:00
void cPlayer :: SetLastBlockActionCnt ( int a_LastBlockActionCnt )
{
m_LastBlockActionCnt = a_LastBlockActionCnt ;
}
2011-11-01 20:09:13 +00:00
void cPlayer :: SetGameMode ( int a_GameMode )
{
2012-01-01 18:45:28 +00:00
if ( ( a_GameMode < 2 ) && ( a_GameMode >= 0 ) )
{
if ( m_GameMode != a_GameMode )
{
cInventory * OldInventory = 0 ;
if ( m_GameMode == 0 )
OldInventory = m_Inventory ;
else
OldInventory = m_CreativeInventory ;
2011-11-10 18:28:21 +00:00
m_GameMode = a_GameMode ;
cPacket_NewInvalidState GameModePacket ;
GameModePacket . m_Reason = 3 ; //GameModeChange
GameModePacket . m_GameMode = ( char ) a_GameMode ; //GameModeChange
m_ClientHandle -> Send ( GameModePacket );
2012-01-01 04:55:17 +00:00
GetInventory (). SendWholeInventory ( m_ClientHandle );
2012-01-01 18:45:28 +00:00
OldInventory -> SetEquippedSlot ( GetInventory (). GetEquippedSlot ());
2011-11-10 18:28:21 +00:00
}
}
}
void cPlayer :: LoginSetGameMode ( int a_GameMode )
{
m_GameMode = a_GameMode ;
2011-11-01 20:09:13 +00:00
}
2011-11-09 22:17:30 +00:00
void cPlayer :: SetIP ( std :: string a_IP )
{
m_IP = a_IP ;
}
2011-11-01 20:09:13 +00:00
2011-10-03 18:41:19 +00:00
void cPlayer :: SendMessage ( const char * a_Message )
{
m_ClientHandle -> Send ( cPacket_Chat ( a_Message ) );
}
void cPlayer :: TeleportTo ( const double & a_PosX , const double & a_PosY , const double & a_PosZ )
{
2011-12-28 02:10:05 +00:00
SetPosition ( a_PosX , a_PosY , a_PosZ );
cPacket_TeleportEntity TeleportEntity ( this );
cRoot :: Get () -> GetServer () -> Broadcast ( TeleportEntity , GetClientHandle () );
2011-10-03 18:41:19 +00:00
cPacket_PlayerPosition PlayerPosition ( this );
2011-12-28 02:10:05 +00:00
2011-10-03 18:41:19 +00:00
m_ClientHandle -> Send ( PlayerPosition );
}
void cPlayer :: MoveTo ( const Vector3d & a_NewPos )
{
// TODO: should do some checks to see if player is not moving through terrain
SetPosition ( a_NewPos );
}
void cPlayer :: SetVisible ( bool a_bVisible )
{
if ( a_bVisible == true && m_bVisible == false ) // Make visible
{
m_bVisible = true ;
SpawnOn ( 0 ); // Spawn on everybody
}
if ( a_bVisible == false && m_bVisible == true )
{
m_bVisible = false ;
cPacket_DestroyEntity DestroyEntity ( this );
2011-10-31 00:52:20 +00:00
cChunk * Chunk = GetWorld () -> GetChunkUnreliable ( m_ChunkX , m_ChunkY , m_ChunkZ );
2011-10-03 18:41:19 +00:00
if ( Chunk )
{
Chunk -> Broadcast ( DestroyEntity ); // Destroy on all clients
}
}
}
void cPlayer :: AddToGroup ( const char * a_GroupName )
{
cGroup * Group = cRoot :: Get () -> GetGroupManager () -> GetGroup ( a_GroupName );
m_pState -> Groups . push_back ( Group );
LOG ( "Added %s to group %s" , m_pState -> PlayerName . c_str (), a_GroupName );
ResolveGroups ();
ResolvePermissions ();
}
bool cPlayer :: CanUseCommand ( const char * a_Command )
{
for ( GroupList :: iterator itr = m_pState -> Groups . begin (); itr != m_pState -> Groups . end (); ++ itr )
{
if ( ( * itr ) -> HasCommand ( a_Command ) ) return true ;
}
return false ;
}
2012-02-01 09:12:54 +00:00
2011-10-03 18:41:19 +00:00
bool cPlayer :: HasPermission ( const char * a_Permission )
{
2012-02-01 09:12:54 +00:00
AStringVector Split = StringSplit ( a_Permission , "." );
2011-10-03 18:41:19 +00:00
PermissionMap Possibilities = m_pState -> ResolvedPermissions ;
// Now search the namespaces
while ( Possibilities . begin () != Possibilities . end () )
{
PermissionMap :: iterator itr = Possibilities . begin ();
if ( itr -> second )
{
2012-02-01 09:12:54 +00:00
AStringVector OtherSplit = StringSplit ( itr -> first , "." );
2011-10-03 18:41:19 +00:00
if ( OtherSplit . size () <= Split . size () )
{
unsigned int i ;
for ( i = 0 ; i < OtherSplit . size (); ++ i )
{
if ( OtherSplit [ i ]. compare ( Split [ i ] ) != 0 )
{
if ( OtherSplit [ i ]. compare ( "*" ) == 0 ) return true ; // WildCard man!! WildCard!
break ;
}
}
if ( i == Split . size () ) return true ;
}
}
Possibilities . erase ( itr );
}
// Nothing that matched :(
return false ;
}
2012-02-01 09:12:54 +00:00
2011-10-03 18:41:19 +00:00
bool cPlayer :: IsInGroup ( const char * a_Group )
{
for ( GroupList :: iterator itr = m_pState -> ResolvedGroups . begin (); itr != m_pState -> ResolvedGroups . end (); ++ itr )
{
if ( strcmp ( a_Group , ( * itr ) -> GetName (). c_str () ) == 0 )
return true ;
}
return false ;
}
void cPlayer :: ResolvePermissions ()
{
m_pState -> ResolvedPermissions . clear (); // Start with an empty map yo~
// Copy all player specific permissions into the resolved permissions map
for ( PermissionMap :: iterator itr = m_pState -> Permissions . begin (); itr != m_pState -> Permissions . end (); ++ itr )
{
m_pState -> ResolvedPermissions [ itr -> first ] = itr -> second ;
}
for ( GroupList :: iterator GroupItr = m_pState -> ResolvedGroups . begin (); GroupItr != m_pState -> ResolvedGroups . end (); ++ GroupItr )
{
const cGroup :: PermissionMap & Permissions = ( * GroupItr ) -> GetPermissions ();
for ( cGroup :: PermissionMap :: const_iterator itr = Permissions . begin (); itr != Permissions . end (); ++ itr )
{
m_pState -> ResolvedPermissions [ itr -> first ] = itr -> second ;
}
}
}
void cPlayer :: ResolveGroups ()
{
// Clear resolved groups first
m_pState -> ResolvedGroups . clear ();
// Get a complete resolved list of all groups the player is in
std :: map < cGroup * , bool > AllGroups ; // Use a map, because it's faster than iterating through a list to find duplicates
GroupList ToIterate ;
for ( GroupList :: iterator GroupItr = m_pState -> Groups . begin (); GroupItr != m_pState -> Groups . end (); ++ GroupItr )
{
ToIterate . push_back ( * GroupItr );
}
while ( ToIterate . begin () != ToIterate . end () )
{
cGroup * CurrentGroup = * ToIterate . begin ();
if ( AllGroups . find ( CurrentGroup ) != AllGroups . end () )
{
LOGERROR ( "ERROR: Player %s is in the same group multiple times (%s). FIX IT!" , m_pState -> PlayerName . c_str (), CurrentGroup -> GetName (). c_str () );
}
else
{
AllGroups [ CurrentGroup ] = true ;
m_pState -> ResolvedGroups . push_back ( CurrentGroup ); // Add group to resolved list
const cGroup :: GroupList & Inherits = CurrentGroup -> GetInherits ();
for ( cGroup :: GroupList :: const_iterator itr = Inherits . begin (); itr != Inherits . end (); ++ itr )
{
if ( AllGroups . find ( * itr ) != AllGroups . end () )
{
LOGERROR ( "ERROR: Player %s is in the same group multiple times due to inheritance (%s). FIX IT!" , m_pState -> PlayerName . c_str (), ( * itr ) -> GetName (). c_str () );
continue ;
}
ToIterate . push_back ( * itr );
}
}
ToIterate . erase ( ToIterate . begin () );
}
}
std :: string cPlayer :: GetColor ()
{
if ( m_Color != '-' )
return cChatColor :: MakeColor ( m_Color );
if ( m_pState -> Groups . size () < 1 )
return cChatColor :: White ;
return ( * m_pState -> Groups . begin ()) -> GetColor ();
}
void cPlayer :: TossItem ( bool a_bDraggingItem , int a_Amount /* = 1 */ )
{
if ( a_bDraggingItem )
{
2012-01-01 18:45:28 +00:00
cItem * Item = GetInventory (). GetWindow () -> GetDraggingItem ();
2011-10-03 18:41:19 +00:00
if ( Item -> m_ItemID > 0 && Item -> m_ItemCount >= a_Amount )
{
float vX = 0 , vY = 0 , vZ = 0 ;
EulerToVector ( - GetRotation (), GetPitch (), vZ , vX , vY );
vY = - vY * 2 + 1.f ;
cPickup * Pickup = new cPickup ( ( int )( GetPosX () * 32 ), ( int )( GetPosY () * 32 ) + ( int )( 1.6f * 32 ), ( int )( GetPosZ () * 32 ), cItem ( Item -> m_ItemID , ( char ) a_Amount , Item -> m_ItemHealth ), vX * 2 , vY * 2 , vZ * 2 );
2011-10-31 00:52:20 +00:00
Pickup -> Initialize ( GetWorld () );
2011-10-03 18:41:19 +00:00
if ( Item -> m_ItemCount > a_Amount )
Item -> m_ItemCount -= ( char ) a_Amount ;
else
Item -> Empty ();
}
return ;
}
// Else drop equipped item
cItem DroppedItem = GetInventory (). GetEquippedItem ();
if ( DroppedItem . m_ItemID > 0 && DroppedItem . m_ItemCount > 0 )
{
DroppedItem . m_ItemCount = 1 ;
if ( GetInventory (). RemoveItem ( DroppedItem ) )
{
DroppedItem . m_ItemCount = 1 ; // RemoveItem decreases the count, so set it to 1 again
float vX = 0 , vY = 0 , vZ = 0 ;
EulerToVector ( - GetRotation (), GetPitch (), vZ , vX , vY );
vY = - vY * 2 + 1.f ;
cPickup * Pickup = new cPickup ( ( int )( GetPosX () * 32 ), ( int )( GetPosY () * 32 ) + ( int )( 1.6f * 32 ), ( int )( GetPosZ () * 32 ), DroppedItem , vX * 2 , vY * 2 , vZ * 2 );
2011-10-31 00:52:20 +00:00
Pickup -> Initialize ( GetWorld () );
2011-10-03 18:41:19 +00:00
}
}
}
2011-12-26 21:54:08 +00:00
bool cPlayer :: MoveToWorld ( const char * a_WorldName )
{
cWorld * World = cRoot :: Get () -> GetWorld ( a_WorldName );
if ( World )
{
/* Remove all links to the old world */
GetWorld () -> RemovePlayer ( this );
GetClientHandle () -> RemoveFromAllChunks ();
cChunk * Chunk = GetWorld () -> GetChunkUnreliable ( m_ChunkX , m_ChunkY , m_ChunkZ );
if ( Chunk )
{
Chunk -> RemoveEntity ( * this );
Chunk -> Broadcast ( cPacket_DestroyEntity ( this ) ); // Remove player entity from all clients in old world
}
/* Add player to all the necessary parts of the new world */
SetWorld ( World );
GetWorld () -> AddPlayer ( this );
MoveToCorrectChunk ( true );
GetClientHandle () -> StreamChunks ();
return true ;
}
return false ;
}
2012-01-30 16:47:26 +00:00
void cPlayer :: LoadPermissionsFromDisk ()
2011-10-03 18:41:19 +00:00
{
2012-01-30 16:47:26 +00:00
m_pState -> Groups . clear ();
m_pState -> Permissions . clear ();
2011-10-03 18:41:19 +00:00
cIniFile IniFile ( "users.ini" );
if ( IniFile . ReadFile () )
{
std :: string Groups = IniFile . GetValue ( m_pState -> PlayerName , "Groups" , "" );
if ( Groups . size () > 0 )
{
2012-02-01 09:12:54 +00:00
AStringVector Split = StringSplit ( Groups , "," );
2011-10-03 18:41:19 +00:00
for ( unsigned int i = 0 ; i < Split . size (); i ++ )
{
AddToGroup ( Split [ i ]. c_str () );
}
}
else
{
AddToGroup ( "Default" );
}
m_Color = IniFile . GetValue ( m_pState -> PlayerName , "Color" , "-" )[ 0 ];
}
else
{
2011-12-27 22:57:33 +00:00
LOGWARN ( "WARNING: Failed to read ini file users.ini" );
2011-10-03 18:41:19 +00:00
AddToGroup ( "Default" );
}
ResolvePermissions ();
2012-01-30 16:47:26 +00:00
}
2012-01-30 22:48:38 +00:00
2012-01-31 00:38:18 +00:00
bool cPlayer :: LoadFromDisk ()
2012-01-30 16:47:26 +00:00
{
LoadPermissionsFromDisk ();
2011-10-03 18:41:19 +00:00
// Log player permissions, cause it's what the cool kids do
LOGINFO ( "Player %s has permissions:" , m_pState -> PlayerName . c_str () );
for ( PermissionMap :: iterator itr = m_pState -> ResolvedPermissions . begin (); itr != m_pState -> ResolvedPermissions . end (); ++ itr )
{
if ( itr -> second ) LOGINFO ( "%s" , itr -> first . c_str () );
}
2012-02-01 13:43:47 +00:00
AString SourceFile ;
Printf ( SourceFile , "players/%s.json" , m_pState -> PlayerName . c_str () );
2011-10-03 18:41:19 +00:00
2012-01-30 22:48:38 +00:00
cFile f ;
if ( ! f . Open ( SourceFile , cFile :: fmRead ))
{
return false ;
}
2011-10-31 21:30:14 +00:00
2012-01-30 22:48:38 +00:00
// Get file size
long FileSize = f . GetSize ();
2011-10-31 21:30:14 +00:00
2012-02-01 13:43:47 +00:00
std :: auto_ptr < char > buffer ( new char [ FileSize ]);
if ( f . Read ( buffer . get (), FileSize ) != FileSize )
2012-01-30 22:48:38 +00:00
{
2012-02-01 14:08:12 +00:00
LOGERROR ( "ERROR READING FROM FILE \" %s \" " , SourceFile . c_str ());
2012-01-30 22:48:38 +00:00
return false ;
}
f . Close ();
2011-11-02 20:19:57 +00:00
2012-01-30 22:48:38 +00:00
Json :: Value root ;
Json :: Reader reader ;
2012-02-01 13:43:47 +00:00
if ( ! reader . parse ( buffer . get (), root , false ))
2012-01-30 22:48:38 +00:00
{
2012-02-01 14:08:12 +00:00
LOGERROR ( "ERROR WHILE PARSING JSON FROM FILE %s" , SourceFile . c_str ());
2012-01-30 22:48:38 +00:00
}
2011-10-31 21:30:14 +00:00
2012-02-01 13:43:47 +00:00
buffer . reset ();
2012-01-30 22:48:38 +00:00
Json :: Value & JSON_PlayerPosition = root [ "position" ];
if ( JSON_PlayerPosition . size () == 3 )
{
m_Pos -> x = JSON_PlayerPosition [( unsigned int ) 0 ]. asDouble ();
m_Pos -> y = JSON_PlayerPosition [( unsigned int ) 1 ]. asDouble ();
m_Pos -> z = JSON_PlayerPosition [( unsigned int ) 2 ]. asDouble ();
2011-10-03 18:41:19 +00:00
}
2012-01-30 22:48:38 +00:00
Json :: Value & JSON_PlayerRotation = root [ "rotation" ];
if ( JSON_PlayerRotation . size () == 3 )
{
m_Rot -> x = ( float ) JSON_PlayerRotation [( unsigned int ) 0 ]. asDouble ();
m_Rot -> y = ( float ) JSON_PlayerRotation [( unsigned int ) 1 ]. asDouble ();
m_Rot -> z = ( float ) JSON_PlayerRotation [( unsigned int ) 2 ]. asDouble ();
}
m_Health = ( short ) root . get ( "health" , 0 ). asInt ();
m_FoodLevel = ( short ) root . get ( "food" , 0 ). asInt ();
m_Inventory -> LoadFromJson ( root [ "inventory" ]);
m_CreativeInventory -> LoadFromJson ( root [ "creativeinventory" ]);
m_pState -> LoadedWorldName = root . get ( "world" , "world" ). asString ();
return true ;
2011-10-03 18:41:19 +00:00
}
2012-01-30 22:48:38 +00:00
2011-10-03 18:41:19 +00:00
bool cPlayer :: SaveToDisk ()
{
2011-11-01 21:57:08 +00:00
cMakeDir :: MakeDir ( "players" );
2011-10-03 18:41:19 +00:00
2011-10-31 21:30:14 +00:00
// create the JSON data
Json :: Value JSON_PlayerPosition ;
JSON_PlayerPosition . append ( Json :: Value ( m_Pos -> x ) );
JSON_PlayerPosition . append ( Json :: Value ( m_Pos -> y ) );
JSON_PlayerPosition . append ( Json :: Value ( m_Pos -> z ) );
Json :: Value JSON_PlayerRotation ;
JSON_PlayerRotation . append ( Json :: Value ( m_Rot -> x ) );
JSON_PlayerRotation . append ( Json :: Value ( m_Rot -> y ) );
JSON_PlayerRotation . append ( Json :: Value ( m_Rot -> z ) );
Json :: Value JSON_Inventory ;
m_Inventory -> SaveToJson ( JSON_Inventory );
2012-01-01 18:45:28 +00:00
Json :: Value JSON_CreativeInventory ;
m_CreativeInventory -> SaveToJson ( JSON_CreativeInventory );
2011-10-31 21:30:14 +00:00
Json :: Value root ;
root [ "position" ] = JSON_PlayerPosition ;
root [ "rotation" ] = JSON_PlayerRotation ;
root [ "inventory" ] = JSON_Inventory ;
2012-01-01 18:45:28 +00:00
root [ "creativeinventory" ] = JSON_CreativeInventory ;
2011-10-31 21:30:14 +00:00
root [ "health" ] = m_Health ;
2011-12-29 15:31:48 +00:00
root [ "food" ] = m_FoodLevel ;
2011-11-02 20:19:57 +00:00
root [ "world" ] = GetWorld () -> GetName ();
2011-10-31 21:30:14 +00:00
Json :: StyledWriter writer ;
std :: string JsonData = writer . write ( root );
2012-02-01 13:43:47 +00:00
AString SourceFile ;
Printf ( SourceFile , "players/%s.json" , m_pState -> PlayerName . c_str () );
2011-10-03 18:41:19 +00:00
2012-01-30 22:48:38 +00:00
cFile f ;
if ( ! f . Open ( SourceFile , cFile :: fmWrite ))
2011-10-03 18:41:19 +00:00
{
2012-02-01 14:08:12 +00:00
LOGERROR ( "ERROR WRITING PLAYER \" %s \" TO FILE \" %s \" - cannot open file" , m_pState -> PlayerName . c_str (), SourceFile . c_str ());
2012-01-30 22:48:38 +00:00
return false ;
2011-10-03 18:41:19 +00:00
}
2012-01-30 22:48:38 +00:00
if ( f . Write ( JsonData . c_str (), JsonData . size ()) != JsonData . size ())
{
2012-02-01 14:08:12 +00:00
LOGERROR ( "ERROR WRITING PLAYER JSON TO FILE \" %s \" " , SourceFile . c_str ());
2012-01-30 22:48:38 +00:00
return false ;
}
return true ;
2011-10-03 18:41:19 +00:00
}
2012-01-30 22:48:38 +00:00
2012-02-01 22:38:03 +00:00
const AString & cPlayer :: GetName ( void ) const
2011-10-03 18:41:19 +00:00
{
2012-02-01 22:38:03 +00:00
return m_pState -> PlayerName ;
2011-10-03 18:41:19 +00:00
}
2012-02-01 22:38:03 +00:00
void cPlayer :: SetName ( const AString & a_Name )
2011-10-03 18:41:19 +00:00
{
m_pState -> PlayerName = a_Name ;
}
2012-02-01 22:38:03 +00:00
2011-10-03 18:41:19 +00:00
const cPlayer :: GroupList & cPlayer :: GetGroups ()
{
return m_pState -> Groups ;
2011-10-26 19:13:49 +00:00
}
2011-11-02 20:19:57 +00:00
2012-02-02 07:47:19 +00:00
2012-01-30 16:47:26 +00:00
cPlayer :: StringList cPlayer :: GetResolvedPermissions ()
{
StringList Permissions ;
const PermissionMap & ResolvedPermissions = m_pState -> ResolvedPermissions ;
for ( PermissionMap :: const_iterator itr = ResolvedPermissions . begin (); itr != ResolvedPermissions . end (); ++ itr )
{
if ( itr -> second ) Permissions . push_back ( itr -> first );
}
return Permissions ;
}
2012-02-02 07:47:19 +00:00
2011-11-02 20:19:57 +00:00
const char * cPlayer :: GetLoadedWorldName ()
{
return m_pState -> LoadedWorldName . c_str ();
2011-12-28 21:00:35 +00:00
}
2012-02-02 07:47:19 +00:00
2011-12-28 21:00:35 +00:00
void cPlayer :: UseEquippedItem ()
{
if ( GetGameMode () != 1 ) //No damage in creative
2012-02-02 07:47:19 +00:00
{
2011-12-28 21:00:35 +00:00
if ( GetInventory (). GetEquippedItem (). DamageItem ())
{
2012-02-01 22:38:03 +00:00
LOG ( "Player %s Broke ID: %i" , GetClientHandle () -> GetUsername (). c_str (), GetInventory (). GetEquippedItem (). m_ItemID );
2011-12-28 21:00:35 +00:00
GetInventory (). RemoveItem ( GetInventory (). GetEquippedItem ());
}
2012-02-02 07:47:19 +00:00
}
}