diff options
Diffstat (limited to 'source/cPlayer.cpp')
-rw-r--r-- | source/cPlayer.cpp | 2124 |
1 files changed, 1062 insertions, 1062 deletions
diff --git a/source/cPlayer.cpp b/source/cPlayer.cpp index 60ae9f888..a7159b2cf 100644 --- a/source/cPlayer.cpp +++ b/source/cPlayer.cpp @@ -1,1062 +1,1062 @@ -
-#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
-
-#include "cPlayer.h"
-#include "cServer.h"
-#include "cCreativeInventory.h"
-#include "cSurvivalInventory.h"
-#include "cClientHandle.h"
-#include "cWorld.h"
-#include "cPickup.h"
-#include "cPluginManager.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"
-#include "cMakeDir.h"
-#include "cTimer.h"
-#include "MersenneTwister.h"
-
-#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_DestroyEntity.h"
-#include "packets/cPacket_Metadata.h"
-#include "packets/cPacket_Chat.h"
-#include "packets/cPacket_NewInvalidState.h"
-#include "packets/cPacket_BlockAction.h"
-
-#include "Vector3d.h"
-#include "Vector3f.h"
-
-#include "../iniFile/iniFile.h"
-#include <json/json.h>
-
-#define float2int(x) ((x)<0 ? ((int)(x))-1 : (int)(x))
-
-
-
-
-
-CLASS_DEFINITION( cPlayer, cPawn );
-
-
-
-
-
-cPlayer::cPlayer(cClientHandle* a_Client, const AString & a_PlayerName)
- : m_GameMode( eGameMode_Survival )
- , m_IP("")
- , m_LastBlockActionTime( 0 )
- , m_LastBlockActionCnt( 0 )
- , 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 )
-{
- LOGD("Created a player object for \"%s\" @ \"%s\" at %p, ID %d",
- a_PlayerName.c_str(), a_Client->GetSocket().GetIPString().c_str(),
- this, GetUniqueID()
- );
- m_EntityType = eEntityType_Player;
- SetMaxHealth(20);
- SetMaxFoodLevel(125);
- m_Inventory = new cSurvivalInventory( this );
- m_CreativeInventory = new cCreativeInventory(this);
- cTimer t1;
- m_LastPlayerListTime = t1.GetNowTime();
-
- m_TimeLastTeleportPacket = cWorld::GetTime();
- m_TimeLastPickupCheck = cWorld::GetTime();
-
- m_PlayerName = a_PlayerName;
- m_bDirtyPosition = true; // So chunks are streamed to player at spawn
-
- if( !LoadFromDisk() )
- {
- m_Inventory->Clear();
- m_CreativeInventory->Clear();
- m_Pos.x = cRoot::Get()->GetDefaultWorld()->GetSpawnX();
- m_Pos.y = cRoot::Get()->GetDefaultWorld()->GetSpawnY();
- m_Pos.z = cRoot::Get()->GetDefaultWorld()->GetSpawnZ();
-
- LOGD("Player \"%s\" is connecting for the first time, spawning at default world spawn {%.2f, %.2f, %.2f}",
- a_PlayerName.c_str(), m_Pos.x, m_Pos.y, m_Pos.z
- );
- }
-}
-
-
-
-
-
-void cPlayer::Initialize( cWorld* a_World )
-{
- cPawn::Initialize( a_World );
- GetWorld()->AddPlayer( this );
-}
-
-
-
-
-
-cPlayer::~cPlayer(void)
-{
- LOG("Deleting cPlayer \"%s\" at %p, ID %d", m_PlayerName.c_str(), this, GetUniqueID());
-
- SaveToDisk();
-
- m_World->RemovePlayer( this );
-
- m_ClientHandle = NULL;
-
- delete m_Inventory;
- m_Inventory = NULL;
-
- delete m_CreativeInventory;
-
- LOG("Player %p deleted", this);
-}
-
-
-
-
-
-void cPlayer::Destroyed()
-{
- CloseWindow(-1);
- m_ClientHandle = NULL;
-}
-
-
-
-
-
-cPacket * cPlayer::GetSpawnPacket(void) const
-{
- LOGD("cPlayer::GetSpawnPacket for \"%s\" at pos {%.2f, %.2f, %.2f}",
- m_PlayerName.c_str(), m_Pos.x, m_Pos.y, m_Pos.z
- );
-
- if (!m_bVisible )
- {
- return NULL;
- }
-
- cPacket_NamedEntitySpawn * SpawnPacket = new cPacket_NamedEntitySpawn;
- SpawnPacket->m_UniqueID = m_UniqueID;
- SpawnPacket->m_PlayerName = m_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);
- short ItemID = (short)m_Inventory->GetEquippedItem().m_ItemID;
- SpawnPacket->m_CurrentItem = (ItemID > 0) ? ItemID : 0; // Unlike -1 in inventory, the named entity packet uses 0 for "none"
- return SpawnPacket;
-}
-
-
-
-
-
-void cPlayer::Tick(float a_Dt)
-{
- if (!m_ClientHandle->IsPlaying())
- {
- // We're not yet in the game, ignore everything
- return;
- }
-
- cPawn::Tick(a_Dt);
-
- if (m_bDirtyOrientation && !m_bDirtyPosition)
- {
- cPacket_EntityLook EntityLook(*this);
- m_World->BroadcastToChunk(m_ChunkX, m_ChunkY, m_ChunkZ, EntityLook, m_ClientHandle );
- cPacket_EntityHeadLook EntityHeadLook(*this);
- m_World->BroadcastToChunk(m_ChunkX, m_ChunkY, m_ChunkZ, EntityHeadLook, m_ClientHandle);
- m_bDirtyOrientation = false;
- }
- else if (m_bDirtyPosition )
- {
- 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 );
- m_World->BroadcastToChunk(m_ChunkX, m_ChunkY, m_ChunkZ, 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);
- m_World->BroadcastToChunk(m_ChunkX, m_ChunkY, m_ChunkZ, 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);
- m_World->BroadcastToChunk(m_ChunkX, m_ChunkY, m_ChunkZ, 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
- {
- m_World->CollectPickupsByPlayer(this);
- }
-
- cTimer t1;
- // Send Player List (Once per m_LastPlayerListTime/1000 ms)
- if (m_LastPlayerListTime + cPlayer::PLAYER_LIST_TIME_MS <= t1.GetNowTime())
- {
- m_World->SendPlayerList(this);
- m_LastPlayerListTime = t1.GetNowTime();
- }
-}
-
-
-
-
-
-void cPlayer::SetTouchGround( bool a_bTouchGround )
-{
- m_bTouchGround = a_bTouchGround;
-
- if( !m_bTouchGround )
- {
- cWorld* World = GetWorld();
- char BlockID = World->GetBlock( float2int(m_Pos.x), float2int(m_Pos.y), float2int(m_Pos.z) );
- 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 )
-{
- if( m_Health < GetMaxHealth() )
- {
- m_Health = (short) MIN(a_Health + m_Health, GetMaxHealth());
-
- cPacket_UpdateHealth Health;
- Health.m_Health = m_Health;
- Health.m_Food = GetFood();
- Health.m_Saturation = GetFoodSaturation();
- m_ClientHandle->Send( Health );
- }
-}
-
-
-
-
-
-bool cPlayer::Feed(short a_Food)
-{
- if (m_FoodLevel >= GetMaxFoodLevel())
- {
- return false;
- }
-
- m_FoodLevel = MIN(a_Food + m_FoodLevel, GetMaxFoodLevel());
-
- cPacket_UpdateHealth Health;
- Health.m_Health = m_Health;
- Health.m_Food = GetFood();
- Health.m_Saturation = GetFoodSaturation();
- m_ClientHandle->Send( Health );
- return true;
-}
-
-
-
-
-
-void cPlayer::TakeDamage( int a_Damage, cEntity* a_Instigator )
-{
- if ( !(m_GameMode == 1) ) {
- cPawn::TakeDamage( a_Damage, a_Instigator );
-
- cPacket_UpdateHealth Health;
- Health.m_Health = m_Health;
- Health.m_Food = GetFood();
- Health.m_Saturation = GetFoodSaturation();
- //TODO: Causes problems sometimes O.o (E.G. Disconnecting when attacked)
- if(m_ClientHandle != 0)
- m_ClientHandle->Send( Health );
- }
-}
-
-
-
-
-
-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
-
- // Puke out all the items
- cItem* Items = m_Inventory->GetSlots();
- cItems Pickups;
- for (unsigned int i = 1; i < m_Inventory->c_NumSlots; ++i)
- {
- if( !Items[i].IsEmpty() )
- {
- Pickups.push_back(Items[i]);
- }
- Items[i].Empty();
- }
- m_World->SpawnItemPickups(Pickups, m_Pos.x, m_Pos.y, m_Pos.z, 10);
- SaveToDisk(); // Save it, yeah the world is a tough place !
-}
-
-
-
-
-
-void cPlayer::Respawn()
-{
- m_Health = GetMaxHealth();
-
- // Create Respawn player packet
- cPacket_Respawn Packet;
- //Set Gamemode for packet by looking at world's gamemode (Need to check players gamemode.)
- //Packet.m_CreativeMode = (char)GetWorld()->GetGameMode();
- Packet.m_CreativeMode = (char)m_GameMode; //Set GameMode packet based on Player's GameMode;
-
- //Send Packet
- m_ClientHandle->Send( Packet );
-
- //Set non Burning
- SetMetaData(NORMAL);
-
- TeleportTo( GetWorld()->GetSpawnX(), GetWorld()->GetSpawnY(), GetWorld()->GetSpawnZ() );
-
- 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 )
-{
- CloseWindow(m_CurrentWindow ? (char)m_CurrentWindow->GetWindowType() : 0);
- a_Window->Open( *this );
- m_CurrentWindow = a_Window;
-}
-
-
-
-
-
-void cPlayer::CloseWindow(char a_WindowType)
-{
- if (a_WindowType == 0)
- {
- // Inventory
- if (
- (m_Inventory->GetWindow()->GetDraggingItem() != NULL) &&
- (m_Inventory->GetWindow()->GetDraggingItem()->m_ItemCount > 0)
- )
- {
- LOG("Player holds item! Dropping it...");
- TossItem( true, m_Inventory->GetWindow()->GetDraggingItem()->m_ItemCount );
- }
-
- //Drop whats in the crafting slots (1, 2, 3, 4)
- cItems Drops;
- for (int i = 1; i <= 4; i++)
- {
- cItem* Item = m_Inventory->GetSlot( i );
- if (!Item->IsEmpty())
- {
- Drops.push_back(*Item);
- }
- Item->Empty();
- }
- float vX = 0, vY = 0, vZ = 0;
- EulerToVector(-GetRotation(), GetPitch(), vZ, vX, vY);
- vY = -vY*2 + 1.f;
- m_World->SpawnItemPickups(Drops, GetPosX(), GetPosY() + 1.6f, GetPosZ(), vX * 2, vY * 2, vZ * 2);
- }
-
- if (m_CurrentWindow)
- {
- // FIXME: If the player entity is destroyed while having a chest window open, the chest will not close
- 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;
- m_World->Broadcast(ChestClose);
- }
-
- m_CurrentWindow->Close( *this );
- }
- m_CurrentWindow = NULL;
-}
-
-
-
-
-
-void cPlayer::SetLastBlockActionTime()
-{
- if (m_World != NULL)
- {
- m_LastBlockActionTime = m_World->GetTime();
- }
-}
-
-
-
-
-
-void cPlayer::SetLastBlockActionCnt( int a_LastBlockActionCnt )
-{
- m_LastBlockActionCnt = a_LastBlockActionCnt;
-}
-
-
-
-
-
-void cPlayer::SetGameMode( eGameMode a_GameMode )
-{
- if ( (a_GameMode < 2) && (a_GameMode >= 0) )
- {
- if (m_GameMode != a_GameMode)
- {
- cInventory *OldInventory = 0;
- if(m_GameMode == eGameMode_Survival)
- OldInventory = m_Inventory;
- else
- OldInventory = m_CreativeInventory;
-
- m_GameMode = a_GameMode;
- cPacket_NewInvalidState GameModePacket;
- GameModePacket.m_Reason = 3; //GameModeChange
- GameModePacket.m_GameMode = (char)a_GameMode; //GameModeChange
- m_ClientHandle->Send ( GameModePacket );
- GetInventory().SendWholeInventory(m_ClientHandle);
-
- OldInventory->SetEquippedSlot(GetInventory().GetEquippedSlot());
- }
- }
-}
-
-
-
-
-
-void cPlayer::LoginSetGameMode( eGameMode a_GameMode )
-{
- m_GameMode = a_GameMode;
-}
-
-
-
-
-
-void cPlayer::SetIP( std::string a_IP )
-{
- m_IP = a_IP;
-}
-
-
-
-
-
-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 )
-{
- SetPosition( a_PosX, a_PosY, a_PosZ );
-
- cPacket_TeleportEntity TeleportEntity( this );
- cRoot::Get()->GetServer()->Broadcast( TeleportEntity, GetClientHandle() );
-
-
- cPacket_PlayerPosition PlayerPosition( this );
-
- 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
- // TODO: Official server refuses position packets too far away from each other, kicking "hacked" clients; we should, too
-
- SetPosition( a_NewPos );
-}
-
-
-
-
-
-void cPlayer::SetVisible( bool a_bVisible )
-{
- if (a_bVisible && !m_bVisible) // Make visible
- {
- m_bVisible = true;
- SpawnOn( NULL ); // Spawn on everybody
- }
- if (!a_bVisible && m_bVisible)
- {
- m_bVisible = false;
- cPacket_DestroyEntity DestroyEntity( this );
- m_World->BroadcastToChunk(m_ChunkX, m_ChunkY, m_ChunkZ, DestroyEntity ); // Destroy on all clients
- }
-}
-
-
-
-
-
-void cPlayer::AddToGroup( const char* a_GroupName )
-{
- cGroup* Group = cRoot::Get()->GetGroupManager()->GetGroup( a_GroupName );
- m_Groups.push_back( Group );
- LOG("Added %s to group %s", m_PlayerName.c_str(), a_GroupName );
- ResolveGroups();
- ResolvePermissions();
-}
-
-
-
-
-
-bool cPlayer::CanUseCommand( const char* a_Command )
-{
- for( GroupList::iterator itr = m_Groups.begin(); itr != m_Groups.end(); ++itr )
- {
- if( (*itr)->HasCommand( a_Command ) ) return true;
- }
- return false;
-}
-
-
-
-
-
-bool cPlayer::HasPermission( const char* a_Permission )
-{
- AStringVector Split = StringSplit( a_Permission, "." );
- PermissionMap Possibilities = m_ResolvedPermissions;
- // Now search the namespaces
- while( Possibilities.begin() != Possibilities.end() )
- {
- PermissionMap::iterator itr = Possibilities.begin();
- if( itr->second )
- {
- AStringVector OtherSplit = StringSplit( itr->first, "." );
- 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;
-}
-
-
-
-
-
-bool cPlayer::IsInGroup( const char* a_Group )
-{
- for( GroupList::iterator itr = m_ResolvedGroups.begin(); itr != m_ResolvedGroups.end(); ++itr )
- {
- if( strcmp( a_Group, (*itr)->GetName().c_str() ) == 0 )
- return true;
- }
- return false;
-}
-
-
-
-
-
-void cPlayer::ResolvePermissions()
-{
- m_ResolvedPermissions.clear(); // Start with an empty map yo~
-
- // Copy all player specific permissions into the resolved permissions map
- for( PermissionMap::iterator itr = m_Permissions.begin(); itr != m_Permissions.end(); ++itr )
- {
- m_ResolvedPermissions[ itr->first ] = itr->second;
- }
-
- for( GroupList::iterator GroupItr = m_ResolvedGroups.begin(); GroupItr != m_ResolvedGroups.end(); ++GroupItr )
- {
- const cGroup::PermissionMap & Permissions = (*GroupItr)->GetPermissions();
- for( cGroup::PermissionMap::const_iterator itr = Permissions.begin(); itr != Permissions.end(); ++itr )
- {
- m_ResolvedPermissions[ itr->first ] = itr->second;
- }
- }
-}
-
-
-
-
-
-void cPlayer::ResolveGroups()
-{
- // Clear resolved groups first
- m_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_Groups.begin(); GroupItr != m_Groups.end(); ++GroupItr )
- {
- ToIterate.push_back( *GroupItr );
- }
- while( ToIterate.begin() != ToIterate.end() )
- {
- cGroup* CurrentGroup = *ToIterate.begin();
- if( AllGroups.find( CurrentGroup ) != AllGroups.end() )
- {
- LOGWARNING("ERROR: Player \"%s\" is in the group multiple times (\"%s\"). Please fix your settings in users.ini!",
- m_PlayerName.c_str(), CurrentGroup->GetName().c_str()
- );
- }
- else
- {
- AllGroups[ CurrentGroup ] = true;
- m_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_PlayerName.c_str(), (*itr)->GetName().c_str() );
- continue;
- }
- ToIterate.push_back( *itr );
- }
- }
- ToIterate.erase( ToIterate.begin() );
- }
-}
-
-
-
-
-
-AString cPlayer::GetColor(void) const
-{
- if ( m_Color != '-' )
- {
- return cChatColor::MakeColor( m_Color );
- }
-
- if ( m_Groups.size() < 1 )
- {
- return cChatColor::White;
- }
-
- return (*m_Groups.begin())->GetColor();
-}
-
-
-
-
-
-void cPlayer::TossItem( bool a_bDraggingItem, int a_Amount /* = 1 */ )
-{
- cItems Drops;
- if (a_bDraggingItem)
- {
- cItem * Item = GetInventory().GetWindow()->GetDraggingItem();
- if (!Item->IsEmpty())
- {
- Drops.push_back(*Item);
- if( Item->m_ItemCount > a_Amount )
- Item->m_ItemCount -= (char)a_Amount;
- else
- Item->Empty();
- }
- }
- else
- {
- // Else drop equipped item
- cItem DroppedItem = GetInventory().GetEquippedItem();
- if (!DroppedItem.IsEmpty())
- {
- DroppedItem.m_ItemCount = 1;
- if (GetInventory().RemoveItem(DroppedItem))
- {
- DroppedItem.m_ItemCount = 1; // RemoveItem decreases the count, so set it to 1 again
- Drops.push_back(DroppedItem);
- }
- }
- }
- float vX = 0, vY = 0, vZ = 0;
- EulerToVector( -GetRotation(), GetPitch(), vZ, vX, vY );
- vY = -vY*2 + 1.f;
- m_World->SpawnItemPickups(Drops, GetPosX(), GetPosY() + 1.6f, GetPosZ(), vX * 2, vY * 2, vZ * 2);
-}
-
-
-
-
-
-bool cPlayer::MoveToWorld( const char* a_WorldName )
-{
- cWorld * World = cRoot::Get()->GetWorld( a_WorldName );
- if ( World )
- {
- /* Remove all links to the old world */
- m_World->RemovePlayer( this );
- m_ClientHandle->RemoveFromAllChunks();
- m_World->RemoveEntityFromChunk(this, m_ChunkX, m_ChunkY, m_ChunkZ);
-
- /* Add player to all the necessary parts of the new world */
- SetWorld( World );
- GetWorld()->AddPlayer( this );
- MoveToCorrectChunk(true);
- GetClientHandle()->StreamChunks();
-
- return true;
- }
-
- return false;
-}
-
-
-
-
-
-void cPlayer::LoadPermissionsFromDisk()
-{
- m_Groups.clear();
- m_Permissions.clear();
-
- cIniFile IniFile("users.ini");
- if( IniFile.ReadFile() )
- {
- std::string Groups = IniFile.GetValue(m_PlayerName, "Groups", "");
- if( Groups.size() > 0 )
- {
- AStringVector Split = StringSplit( Groups, "," );
- for( unsigned int i = 0; i < Split.size(); i++ )
- {
- AddToGroup( Split[i].c_str() );
- }
- }
- else
- {
- AddToGroup("Default");
- }
-
- m_Color = IniFile.GetValue(m_PlayerName, "Color", "-")[0];
- }
- else
- {
- LOGWARN("WARNING: Failed to read ini file users.ini");
- AddToGroup("Default");
- }
- ResolvePermissions();
-}
-
-
-
-
-bool cPlayer::LoadFromDisk()
-{
- LoadPermissionsFromDisk();
-
- // Log player permissions, cause it's what the cool kids do
- LOGINFO("Player %s has permissions:", m_PlayerName.c_str() );
- for( PermissionMap::iterator itr = m_ResolvedPermissions.begin(); itr != m_ResolvedPermissions.end(); ++itr )
- {
- if( itr->second ) LOGINFO("%s", itr->first.c_str() );
- }
-
- AString SourceFile;
- Printf(SourceFile, "players/%s.json", m_PlayerName.c_str() );
-
- cFile f;
- if (!f.Open(SourceFile, cFile::fmRead))
- {
- return false;
- }
-
- AString buffer;
- if (f.ReadRestOfFile(buffer) != f.GetSize())
- {
- LOGERROR("ERROR READING FROM FILE \"%s\"", SourceFile.c_str());
- return false;
- }
- f.Close();
-
- Json::Value root;
- Json::Reader reader;
- if (!reader.parse(buffer, root, false))
- {
- LOGERROR("ERROR WHILE PARSING JSON FROM FILE %s", SourceFile.c_str());
- }
-
- 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();
- }
-
- 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_LoadedWorldName = root.get("world", "world").asString();
-
- LOGD("Player \"%s\" was read from file, spawning at {%.2f, %.2f, %.2f} in world \"%s\"",
- m_PlayerName.c_str(), m_Pos.x, m_Pos.y, m_Pos.z, m_LoadedWorldName.c_str()
- );
-
- return true;
-}
-
-
-
-
-
-bool cPlayer::SaveToDisk()
-{
- cMakeDir::MakeDir("players");
-
- // 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 );
-
- Json::Value JSON_CreativeInventory;
- m_CreativeInventory->SaveToJson( JSON_CreativeInventory );
-
- Json::Value root;
- root["position"] = JSON_PlayerPosition;
- root["rotation"] = JSON_PlayerRotation;
- root["inventory"] = JSON_Inventory;
- root["creativeinventory"] = JSON_CreativeInventory;
- root["health"] = m_Health;
- root["food"] = m_FoodLevel;
- root["world"] = GetWorld()->GetName();
-
- Json::StyledWriter writer;
- std::string JsonData = writer.write( root );
-
- AString SourceFile;
- Printf(SourceFile, "players/%s.json", m_PlayerName.c_str() );
-
- cFile f;
- if (!f.Open(SourceFile, cFile::fmWrite))
- {
- LOGERROR("ERROR WRITING PLAYER \"%s\" TO FILE \"%s\" - cannot open file", m_PlayerName.c_str(), SourceFile.c_str());
- return false;
- }
- if (f.Write(JsonData.c_str(), JsonData.size()) != (int)JsonData.size())
- {
- LOGERROR("ERROR WRITING PLAYER JSON TO FILE \"%s\"", SourceFile.c_str());
- return false;
- }
- return true;
-}
-
-
-
-
-
-cPlayer::StringList cPlayer::GetResolvedPermissions()
-{
- StringList Permissions;
-
- const PermissionMap& ResolvedPermissions = m_ResolvedPermissions;
- for( PermissionMap::const_iterator itr = ResolvedPermissions.begin(); itr != ResolvedPermissions.end(); ++itr )
- {
- if( itr->second ) Permissions.push_back( itr->first );
- }
-
- return Permissions;
-}
-
-
-
-
-
-void cPlayer::UseEquippedItem()
-{
- if(GetGameMode() != 1) //No damage in creative
- {
- if (GetInventory().GetEquippedItem().DamageItem())
- {
- LOG("Player %s Broke ID: %i", GetClientHandle()->GetUsername().c_str(), GetInventory().GetEquippedItem().m_ItemID);
- GetInventory().RemoveItem( GetInventory().GetEquippedItem());
- }
- }
-}
-
-
-
-
-
-bool cPlayer::EatItem(int a_ItemType)
-{
- // TODO: Handle hunger
- switch (a_ItemType)
- {
- case E_ITEM_APPLE: return Feed(24); // 2 food bars
- case E_ITEM_GOLDEN_APPLE: return Feed(60); // 5 food
- case E_ITEM_MUSHROOM_SOUP: return Feed(48); // 4 food
- case E_ITEM_BREAD: return Feed(30); // 2.5 food
- case E_ITEM_RAW_MEAT: return Feed(18); // 1.5 food
- case E_ITEM_COOKED_MEAT: return Feed(48); // 4 food
- case E_ITEM_RAW_FISH: return Feed(12); // 1 food
- case E_ITEM_COOKED_FISH: return Feed(30); // 2.5 food
- case E_ITEM_COOKED_CHICKEN: return Feed(36); // 3 food
- case E_ITEM_RAW_BEEF: return Feed(18); // 1.5 food
- case E_ITEM_STEAK: return Feed(48); // 4 food
- case E_ITEM_RAW_CHICKEN:
- {
- if (!Feed(12)) // 1 food
- {
- return false;
- }
- // TODO: A random chance to get food-poisoned
- return true;
- }
-
- case E_ITEM_ROTTEN_FLESH:
- {
- if (!Feed(24))
- {
- return false;
- }
- // TODO: Food-poisoning
- return true;
- }
- }
- return false;
-}
-
-
-
-
+ +#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules + +#include "cPlayer.h" +#include "cServer.h" +#include "cCreativeInventory.h" +#include "cSurvivalInventory.h" +#include "cClientHandle.h" +#include "cWorld.h" +#include "cPickup.h" +#include "cPluginManager.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" +#include "cMakeDir.h" +#include "cTimer.h" +#include "MersenneTwister.h" + +#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_DestroyEntity.h" +#include "packets/cPacket_Metadata.h" +#include "packets/cPacket_Chat.h" +#include "packets/cPacket_NewInvalidState.h" +#include "packets/cPacket_BlockAction.h" + +#include "Vector3d.h" +#include "Vector3f.h" + +#include "../iniFile/iniFile.h" +#include <json/json.h> + +#define float2int(x) ((x)<0 ? ((int)(x))-1 : (int)(x)) + + + + + +CLASS_DEFINITION( cPlayer, cPawn ); + + + + + +cPlayer::cPlayer(cClientHandle* a_Client, const AString & a_PlayerName) + : m_GameMode( eGameMode_Survival ) + , m_IP("") + , m_LastBlockActionTime( 0 ) + , m_LastBlockActionCnt( 0 ) + , 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 ) +{ + LOGD("Created a player object for \"%s\" @ \"%s\" at %p, ID %d", + a_PlayerName.c_str(), a_Client->GetSocket().GetIPString().c_str(), + this, GetUniqueID() + ); + m_EntityType = eEntityType_Player; + SetMaxHealth(20); + SetMaxFoodLevel(125); + m_Inventory = new cSurvivalInventory( this ); + m_CreativeInventory = new cCreativeInventory(this); + cTimer t1; + m_LastPlayerListTime = t1.GetNowTime(); + + m_TimeLastTeleportPacket = cWorld::GetTime(); + m_TimeLastPickupCheck = cWorld::GetTime(); + + m_PlayerName = a_PlayerName; + m_bDirtyPosition = true; // So chunks are streamed to player at spawn + + if( !LoadFromDisk() ) + { + m_Inventory->Clear(); + m_CreativeInventory->Clear(); + m_Pos.x = cRoot::Get()->GetDefaultWorld()->GetSpawnX(); + m_Pos.y = cRoot::Get()->GetDefaultWorld()->GetSpawnY(); + m_Pos.z = cRoot::Get()->GetDefaultWorld()->GetSpawnZ(); + + LOGD("Player \"%s\" is connecting for the first time, spawning at default world spawn {%.2f, %.2f, %.2f}", + a_PlayerName.c_str(), m_Pos.x, m_Pos.y, m_Pos.z + ); + } +} + + + + + +void cPlayer::Initialize( cWorld* a_World ) +{ + cPawn::Initialize( a_World ); + GetWorld()->AddPlayer( this ); +} + + + + + +cPlayer::~cPlayer(void) +{ + LOG("Deleting cPlayer \"%s\" at %p, ID %d", m_PlayerName.c_str(), this, GetUniqueID()); + + SaveToDisk(); + + m_World->RemovePlayer( this ); + + m_ClientHandle = NULL; + + delete m_Inventory; + m_Inventory = NULL; + + delete m_CreativeInventory; + + LOG("Player %p deleted", this); +} + + + + + +void cPlayer::Destroyed() +{ + CloseWindow(-1); + m_ClientHandle = NULL; +} + + + + + +cPacket * cPlayer::GetSpawnPacket(void) const +{ + LOGD("cPlayer::GetSpawnPacket for \"%s\" at pos {%.2f, %.2f, %.2f}", + m_PlayerName.c_str(), m_Pos.x, m_Pos.y, m_Pos.z + ); + + if (!m_bVisible ) + { + return NULL; + } + + cPacket_NamedEntitySpawn * SpawnPacket = new cPacket_NamedEntitySpawn; + SpawnPacket->m_UniqueID = m_UniqueID; + SpawnPacket->m_PlayerName = m_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); + short ItemID = (short)m_Inventory->GetEquippedItem().m_ItemID; + SpawnPacket->m_CurrentItem = (ItemID > 0) ? ItemID : 0; // Unlike -1 in inventory, the named entity packet uses 0 for "none" + return SpawnPacket; +} + + + + + +void cPlayer::Tick(float a_Dt) +{ + if (!m_ClientHandle->IsPlaying()) + { + // We're not yet in the game, ignore everything + return; + } + + cPawn::Tick(a_Dt); + + if (m_bDirtyOrientation && !m_bDirtyPosition) + { + cPacket_EntityLook EntityLook(*this); + m_World->BroadcastToChunk(m_ChunkX, m_ChunkY, m_ChunkZ, EntityLook, m_ClientHandle ); + cPacket_EntityHeadLook EntityHeadLook(*this); + m_World->BroadcastToChunk(m_ChunkX, m_ChunkY, m_ChunkZ, EntityHeadLook, m_ClientHandle); + m_bDirtyOrientation = false; + } + else if (m_bDirtyPosition ) + { + 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 ); + m_World->BroadcastToChunk(m_ChunkX, m_ChunkY, m_ChunkZ, 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); + m_World->BroadcastToChunk(m_ChunkX, m_ChunkY, m_ChunkZ, 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); + m_World->BroadcastToChunk(m_ChunkX, m_ChunkY, m_ChunkZ, 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 + { + m_World->CollectPickupsByPlayer(this); + } + + cTimer t1; + // Send Player List (Once per m_LastPlayerListTime/1000 ms) + if (m_LastPlayerListTime + cPlayer::PLAYER_LIST_TIME_MS <= t1.GetNowTime()) + { + m_World->SendPlayerList(this); + m_LastPlayerListTime = t1.GetNowTime(); + } +} + + + + + +void cPlayer::SetTouchGround( bool a_bTouchGround ) +{ + m_bTouchGround = a_bTouchGround; + + if( !m_bTouchGround ) + { + cWorld* World = GetWorld(); + char BlockID = World->GetBlock( float2int(m_Pos.x), float2int(m_Pos.y), float2int(m_Pos.z) ); + 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 ) +{ + if( m_Health < GetMaxHealth() ) + { + m_Health = (short) MIN(a_Health + m_Health, GetMaxHealth()); + + cPacket_UpdateHealth Health; + Health.m_Health = m_Health; + Health.m_Food = GetFood(); + Health.m_Saturation = GetFoodSaturation(); + m_ClientHandle->Send( Health ); + } +} + + + + + +bool cPlayer::Feed(short a_Food) +{ + if (m_FoodLevel >= GetMaxFoodLevel()) + { + return false; + } + + m_FoodLevel = MIN(a_Food + m_FoodLevel, GetMaxFoodLevel()); + + cPacket_UpdateHealth Health; + Health.m_Health = m_Health; + Health.m_Food = GetFood(); + Health.m_Saturation = GetFoodSaturation(); + m_ClientHandle->Send( Health ); + return true; +} + + + + + +void cPlayer::TakeDamage( int a_Damage, cEntity* a_Instigator ) +{ + if ( !(m_GameMode == 1) ) { + cPawn::TakeDamage( a_Damage, a_Instigator ); + + cPacket_UpdateHealth Health; + Health.m_Health = m_Health; + Health.m_Food = GetFood(); + Health.m_Saturation = GetFoodSaturation(); + //TODO: Causes problems sometimes O.o (E.G. Disconnecting when attacked) + if(m_ClientHandle != 0) + m_ClientHandle->Send( Health ); + } +} + + + + + +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 + + // Puke out all the items + cItem* Items = m_Inventory->GetSlots(); + cItems Pickups; + for (unsigned int i = 1; i < m_Inventory->c_NumSlots; ++i) + { + if( !Items[i].IsEmpty() ) + { + Pickups.push_back(Items[i]); + } + Items[i].Empty(); + } + m_World->SpawnItemPickups(Pickups, m_Pos.x, m_Pos.y, m_Pos.z, 10); + SaveToDisk(); // Save it, yeah the world is a tough place ! +} + + + + + +void cPlayer::Respawn() +{ + m_Health = GetMaxHealth(); + + // Create Respawn player packet + cPacket_Respawn Packet; + //Set Gamemode for packet by looking at world's gamemode (Need to check players gamemode.) + //Packet.m_CreativeMode = (char)GetWorld()->GetGameMode(); + Packet.m_CreativeMode = (char)m_GameMode; //Set GameMode packet based on Player's GameMode; + + //Send Packet + m_ClientHandle->Send( Packet ); + + //Set non Burning + SetMetaData(NORMAL); + + TeleportTo( GetWorld()->GetSpawnX(), GetWorld()->GetSpawnY(), GetWorld()->GetSpawnZ() ); + + 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 ) +{ + CloseWindow(m_CurrentWindow ? (char)m_CurrentWindow->GetWindowType() : 0); + a_Window->Open( *this ); + m_CurrentWindow = a_Window; +} + + + + + +void cPlayer::CloseWindow(char a_WindowType) +{ + if (a_WindowType == 0) + { + // Inventory + if ( + (m_Inventory->GetWindow()->GetDraggingItem() != NULL) && + (m_Inventory->GetWindow()->GetDraggingItem()->m_ItemCount > 0) + ) + { + LOG("Player holds item! Dropping it..."); + TossItem( true, m_Inventory->GetWindow()->GetDraggingItem()->m_ItemCount ); + } + + //Drop whats in the crafting slots (1, 2, 3, 4) + cItems Drops; + for (int i = 1; i <= 4; i++) + { + cItem* Item = m_Inventory->GetSlot( i ); + if (!Item->IsEmpty()) + { + Drops.push_back(*Item); + } + Item->Empty(); + } + float vX = 0, vY = 0, vZ = 0; + EulerToVector(-GetRotation(), GetPitch(), vZ, vX, vY); + vY = -vY*2 + 1.f; + m_World->SpawnItemPickups(Drops, GetPosX(), GetPosY() + 1.6f, GetPosZ(), vX * 2, vY * 2, vZ * 2); + } + + if (m_CurrentWindow) + { + // FIXME: If the player entity is destroyed while having a chest window open, the chest will not close + 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; + m_World->Broadcast(ChestClose); + } + + m_CurrentWindow->Close( *this ); + } + m_CurrentWindow = NULL; +} + + + + + +void cPlayer::SetLastBlockActionTime() +{ + if (m_World != NULL) + { + m_LastBlockActionTime = m_World->GetTime(); + } +} + + + + + +void cPlayer::SetLastBlockActionCnt( int a_LastBlockActionCnt ) +{ + m_LastBlockActionCnt = a_LastBlockActionCnt; +} + + + + + +void cPlayer::SetGameMode( eGameMode a_GameMode ) +{ + if ( (a_GameMode < 2) && (a_GameMode >= 0) ) + { + if (m_GameMode != a_GameMode) + { + cInventory *OldInventory = 0; + if(m_GameMode == eGameMode_Survival) + OldInventory = m_Inventory; + else + OldInventory = m_CreativeInventory; + + m_GameMode = a_GameMode; + cPacket_NewInvalidState GameModePacket; + GameModePacket.m_Reason = 3; //GameModeChange + GameModePacket.m_GameMode = (char)a_GameMode; //GameModeChange + m_ClientHandle->Send ( GameModePacket ); + GetInventory().SendWholeInventory(m_ClientHandle); + + OldInventory->SetEquippedSlot(GetInventory().GetEquippedSlot()); + } + } +} + + + + + +void cPlayer::LoginSetGameMode( eGameMode a_GameMode ) +{ + m_GameMode = a_GameMode; +} + + + + + +void cPlayer::SetIP( std::string a_IP ) +{ + m_IP = a_IP; +} + + + + + +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 ) +{ + SetPosition( a_PosX, a_PosY, a_PosZ ); + + cPacket_TeleportEntity TeleportEntity( this ); + cRoot::Get()->GetServer()->Broadcast( TeleportEntity, GetClientHandle() ); + + + cPacket_PlayerPosition PlayerPosition( this ); + + 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 + // TODO: Official server refuses position packets too far away from each other, kicking "hacked" clients; we should, too + + SetPosition( a_NewPos ); +} + + + + + +void cPlayer::SetVisible( bool a_bVisible ) +{ + if (a_bVisible && !m_bVisible) // Make visible + { + m_bVisible = true; + SpawnOn( NULL ); // Spawn on everybody + } + if (!a_bVisible && m_bVisible) + { + m_bVisible = false; + cPacket_DestroyEntity DestroyEntity( this ); + m_World->BroadcastToChunk(m_ChunkX, m_ChunkY, m_ChunkZ, DestroyEntity ); // Destroy on all clients + } +} + + + + + +void cPlayer::AddToGroup( const char* a_GroupName ) +{ + cGroup* Group = cRoot::Get()->GetGroupManager()->GetGroup( a_GroupName ); + m_Groups.push_back( Group ); + LOG("Added %s to group %s", m_PlayerName.c_str(), a_GroupName ); + ResolveGroups(); + ResolvePermissions(); +} + + + + + +bool cPlayer::CanUseCommand( const char* a_Command ) +{ + for( GroupList::iterator itr = m_Groups.begin(); itr != m_Groups.end(); ++itr ) + { + if( (*itr)->HasCommand( a_Command ) ) return true; + } + return false; +} + + + + + +bool cPlayer::HasPermission( const char* a_Permission ) +{ + AStringVector Split = StringSplit( a_Permission, "." ); + PermissionMap Possibilities = m_ResolvedPermissions; + // Now search the namespaces + while( Possibilities.begin() != Possibilities.end() ) + { + PermissionMap::iterator itr = Possibilities.begin(); + if( itr->second ) + { + AStringVector OtherSplit = StringSplit( itr->first, "." ); + 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; +} + + + + + +bool cPlayer::IsInGroup( const char* a_Group ) +{ + for( GroupList::iterator itr = m_ResolvedGroups.begin(); itr != m_ResolvedGroups.end(); ++itr ) + { + if( strcmp( a_Group, (*itr)->GetName().c_str() ) == 0 ) + return true; + } + return false; +} + + + + + +void cPlayer::ResolvePermissions() +{ + m_ResolvedPermissions.clear(); // Start with an empty map yo~ + + // Copy all player specific permissions into the resolved permissions map + for( PermissionMap::iterator itr = m_Permissions.begin(); itr != m_Permissions.end(); ++itr ) + { + m_ResolvedPermissions[ itr->first ] = itr->second; + } + + for( GroupList::iterator GroupItr = m_ResolvedGroups.begin(); GroupItr != m_ResolvedGroups.end(); ++GroupItr ) + { + const cGroup::PermissionMap & Permissions = (*GroupItr)->GetPermissions(); + for( cGroup::PermissionMap::const_iterator itr = Permissions.begin(); itr != Permissions.end(); ++itr ) + { + m_ResolvedPermissions[ itr->first ] = itr->second; + } + } +} + + + + + +void cPlayer::ResolveGroups() +{ + // Clear resolved groups first + m_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_Groups.begin(); GroupItr != m_Groups.end(); ++GroupItr ) + { + ToIterate.push_back( *GroupItr ); + } + while( ToIterate.begin() != ToIterate.end() ) + { + cGroup* CurrentGroup = *ToIterate.begin(); + if( AllGroups.find( CurrentGroup ) != AllGroups.end() ) + { + LOGWARNING("ERROR: Player \"%s\" is in the group multiple times (\"%s\"). Please fix your settings in users.ini!", + m_PlayerName.c_str(), CurrentGroup->GetName().c_str() + ); + } + else + { + AllGroups[ CurrentGroup ] = true; + m_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_PlayerName.c_str(), (*itr)->GetName().c_str() ); + continue; + } + ToIterate.push_back( *itr ); + } + } + ToIterate.erase( ToIterate.begin() ); + } +} + + + + + +AString cPlayer::GetColor(void) const +{ + if ( m_Color != '-' ) + { + return cChatColor::MakeColor( m_Color ); + } + + if ( m_Groups.size() < 1 ) + { + return cChatColor::White; + } + + return (*m_Groups.begin())->GetColor(); +} + + + + + +void cPlayer::TossItem( bool a_bDraggingItem, int a_Amount /* = 1 */ ) +{ + cItems Drops; + if (a_bDraggingItem) + { + cItem * Item = GetInventory().GetWindow()->GetDraggingItem(); + if (!Item->IsEmpty()) + { + Drops.push_back(*Item); + if( Item->m_ItemCount > a_Amount ) + Item->m_ItemCount -= (char)a_Amount; + else + Item->Empty(); + } + } + else + { + // Else drop equipped item + cItem DroppedItem = GetInventory().GetEquippedItem(); + if (!DroppedItem.IsEmpty()) + { + DroppedItem.m_ItemCount = 1; + if (GetInventory().RemoveItem(DroppedItem)) + { + DroppedItem.m_ItemCount = 1; // RemoveItem decreases the count, so set it to 1 again + Drops.push_back(DroppedItem); + } + } + } + float vX = 0, vY = 0, vZ = 0; + EulerToVector( -GetRotation(), GetPitch(), vZ, vX, vY ); + vY = -vY*2 + 1.f; + m_World->SpawnItemPickups(Drops, GetPosX(), GetPosY() + 1.6f, GetPosZ(), vX * 2, vY * 2, vZ * 2); +} + + + + + +bool cPlayer::MoveToWorld( const char* a_WorldName ) +{ + cWorld * World = cRoot::Get()->GetWorld( a_WorldName ); + if ( World ) + { + /* Remove all links to the old world */ + m_World->RemovePlayer( this ); + m_ClientHandle->RemoveFromAllChunks(); + m_World->RemoveEntityFromChunk(this, m_ChunkX, m_ChunkY, m_ChunkZ); + + /* Add player to all the necessary parts of the new world */ + SetWorld( World ); + GetWorld()->AddPlayer( this ); + MoveToCorrectChunk(true); + GetClientHandle()->StreamChunks(); + + return true; + } + + return false; +} + + + + + +void cPlayer::LoadPermissionsFromDisk() +{ + m_Groups.clear(); + m_Permissions.clear(); + + cIniFile IniFile("users.ini"); + if( IniFile.ReadFile() ) + { + std::string Groups = IniFile.GetValue(m_PlayerName, "Groups", ""); + if( Groups.size() > 0 ) + { + AStringVector Split = StringSplit( Groups, "," ); + for( unsigned int i = 0; i < Split.size(); i++ ) + { + AddToGroup( Split[i].c_str() ); + } + } + else + { + AddToGroup("Default"); + } + + m_Color = IniFile.GetValue(m_PlayerName, "Color", "-")[0]; + } + else + { + LOGWARN("WARNING: Failed to read ini file users.ini"); + AddToGroup("Default"); + } + ResolvePermissions(); +} + + + + +bool cPlayer::LoadFromDisk() +{ + LoadPermissionsFromDisk(); + + // Log player permissions, cause it's what the cool kids do + LOGINFO("Player %s has permissions:", m_PlayerName.c_str() ); + for( PermissionMap::iterator itr = m_ResolvedPermissions.begin(); itr != m_ResolvedPermissions.end(); ++itr ) + { + if( itr->second ) LOGINFO("%s", itr->first.c_str() ); + } + + AString SourceFile; + Printf(SourceFile, "players/%s.json", m_PlayerName.c_str() ); + + cFile f; + if (!f.Open(SourceFile, cFile::fmRead)) + { + return false; + } + + AString buffer; + if (f.ReadRestOfFile(buffer) != f.GetSize()) + { + LOGERROR("ERROR READING FROM FILE \"%s\"", SourceFile.c_str()); + return false; + } + f.Close(); + + Json::Value root; + Json::Reader reader; + if (!reader.parse(buffer, root, false)) + { + LOGERROR("ERROR WHILE PARSING JSON FROM FILE %s", SourceFile.c_str()); + } + + 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(); + } + + 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_LoadedWorldName = root.get("world", "world").asString(); + + LOGD("Player \"%s\" was read from file, spawning at {%.2f, %.2f, %.2f} in world \"%s\"", + m_PlayerName.c_str(), m_Pos.x, m_Pos.y, m_Pos.z, m_LoadedWorldName.c_str() + ); + + return true; +} + + + + + +bool cPlayer::SaveToDisk() +{ + cMakeDir::MakeDir("players"); + + // 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 ); + + Json::Value JSON_CreativeInventory; + m_CreativeInventory->SaveToJson( JSON_CreativeInventory ); + + Json::Value root; + root["position"] = JSON_PlayerPosition; + root["rotation"] = JSON_PlayerRotation; + root["inventory"] = JSON_Inventory; + root["creativeinventory"] = JSON_CreativeInventory; + root["health"] = m_Health; + root["food"] = m_FoodLevel; + root["world"] = GetWorld()->GetName(); + + Json::StyledWriter writer; + std::string JsonData = writer.write( root ); + + AString SourceFile; + Printf(SourceFile, "players/%s.json", m_PlayerName.c_str() ); + + cFile f; + if (!f.Open(SourceFile, cFile::fmWrite)) + { + LOGERROR("ERROR WRITING PLAYER \"%s\" TO FILE \"%s\" - cannot open file", m_PlayerName.c_str(), SourceFile.c_str()); + return false; + } + if (f.Write(JsonData.c_str(), JsonData.size()) != (int)JsonData.size()) + { + LOGERROR("ERROR WRITING PLAYER JSON TO FILE \"%s\"", SourceFile.c_str()); + return false; + } + return true; +} + + + + + +cPlayer::StringList cPlayer::GetResolvedPermissions() +{ + StringList Permissions; + + const PermissionMap& ResolvedPermissions = m_ResolvedPermissions; + for( PermissionMap::const_iterator itr = ResolvedPermissions.begin(); itr != ResolvedPermissions.end(); ++itr ) + { + if( itr->second ) Permissions.push_back( itr->first ); + } + + return Permissions; +} + + + + + +void cPlayer::UseEquippedItem() +{ + if(GetGameMode() != 1) //No damage in creative + { + if (GetInventory().GetEquippedItem().DamageItem()) + { + LOG("Player %s Broke ID: %i", GetClientHandle()->GetUsername().c_str(), GetInventory().GetEquippedItem().m_ItemID); + GetInventory().RemoveItem( GetInventory().GetEquippedItem()); + } + } +} + + + + + +bool cPlayer::EatItem(int a_ItemType) +{ + // TODO: Handle hunger + switch (a_ItemType) + { + case E_ITEM_APPLE: return Feed(24); // 2 food bars + case E_ITEM_GOLDEN_APPLE: return Feed(60); // 5 food + case E_ITEM_MUSHROOM_SOUP: return Feed(48); // 4 food + case E_ITEM_BREAD: return Feed(30); // 2.5 food + case E_ITEM_RAW_MEAT: return Feed(18); // 1.5 food + case E_ITEM_COOKED_MEAT: return Feed(48); // 4 food + case E_ITEM_RAW_FISH: return Feed(12); // 1 food + case E_ITEM_COOKED_FISH: return Feed(30); // 2.5 food + case E_ITEM_COOKED_CHICKEN: return Feed(36); // 3 food + case E_ITEM_RAW_BEEF: return Feed(18); // 1.5 food + case E_ITEM_STEAK: return Feed(48); // 4 food + case E_ITEM_RAW_CHICKEN: + { + if (!Feed(12)) // 1 food + { + return false; + } + // TODO: A random chance to get food-poisoned + return true; + } + + case E_ITEM_ROTTEN_FLESH: + { + if (!Feed(24)) + { + return false; + } + // TODO: Food-poisoning + return true; + } + } + return false; +} + + + + |