From 92c59963f82f81aa3202657e7fdbb2592924ede3 Mon Sep 17 00:00:00 2001 From: "cedeel@gmail.com" Date: Thu, 14 Jun 2012 13:06:06 +0000 Subject: Attempt to bring sanity to newlines across systems. git-svn-id: http://mc-server.googlecode.com/svn/trunk@606 0a769ca7-a7f5-676a-18bf-c427514a06d6 --- source/cPlayer.cpp | 2124 ++++++++++++++++++++++++++-------------------------- 1 file changed, 1062 insertions(+), 1062 deletions(-) (limited to 'source/cPlayer.cpp') 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 - -#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 + +#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; +} + + + + -- cgit v1.2.3