summaryrefslogtreecommitdiffstats
path: root/source/cPlayer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'source/cPlayer.cpp')
-rw-r--r--source/cPlayer.cpp2124
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;
+}
+
+
+
+