1
Fork 0
mirror of https://github.com/SapphireServer/Sapphire.git synced 2025-04-24 13:47:46 +00:00
sapphire/src/world/Manager/PlayerMgr.cpp

542 lines
19 KiB
C++
Raw Normal View History

#include "PlayerMgr.h"
#include <Service.h>
#include <Exd/ExdData.h>
2023-01-27 11:13:57 +01:00
#include <Territory/Land.h>
#include <Manager/TerritoryMgr.h>
#include <Manager/AchievementMgr.h>
2023-01-27 11:13:57 +01:00
#include <Manager/PartyMgr.h>
#include <Manager/HousingMgr.h>
#include <Manager/QuestMgr.h>
#include <Script/ScriptMgr.h>
#include <Common.h>
#include <Network/GameConnection.h>
#include <Network/CommonActorControl.h>
#include <Network/PacketDef/Zone/ServerZoneDef.h>
#include <Network/PacketWrappers/ActorControlPacket.h>
#include <Network/PacketWrappers/ActorControlSelfPacket.h>
#include <Network/PacketWrappers/ActorControlTargetPacket.h>
#include <Network/PacketWrappers/InitZonePacket.h>
#include <Network/PacketWrappers/ModelEquipPacket.h>
#include <Network/PacketWrappers/PlayerSetupPacket.h>
2023-02-20 15:25:57 +01:00
#include <Network/PacketWrappers/ConditionPacket.h>
2023-02-25 15:31:57 +01:00
#include <Network/PacketWrappers/RestingPacket.h>
#include <Network/PacketWrappers/ServerNoticePacket.h>
#include <Network/PacketWrappers/ChatPacket.h>
#include <Network/PacketWrappers/HudParamPacket.h>
#include <Actor/Player.h>
#include <Actor/BNpc.h>
#include <Inventory/Item.h>
#include <Util/UtilMath.h>
using namespace Sapphire;
using namespace Sapphire::World::Manager;
using namespace Sapphire::Network::Packets;
using namespace Sapphire::Network::Packets::WorldPackets::Server;
using namespace Sapphire::Network::ActorControl;
void PlayerMgr::onOnlineStatusChanged( Entity::Player& player, bool updateProfile )
{
auto statusPacket = makeZonePacket< FFXIVIpcSetOnlineStatus >( player.getId() );
statusPacket->data().onlineStatusFlags = player.getFullOnlineStatusMask();
server().queueForPlayer( player.getCharacterId(), statusPacket );
if( updateProfile )
{
auto searchInfoPacket = makeZonePacket< FFXIVIpcSetProfileResult >( player.getId());
searchInfoPacket->data().OnlineStatus = player.getFullOnlineStatusMask();
searchInfoPacket->data().Region = player.getSearchSelectRegion();
strcpy( searchInfoPacket->data().SearchComment, player.getSearchMessage());
server().queueForPlayer( player.getCharacterId(), searchInfoPacket );
}
server().queueForPlayers( player.getInRangePlayerIds( true ),
makeActorControl( player.getId(), SetStatusIcon, static_cast< uint8_t >( player.getOnlineStatus() ) ) );
}
void PlayerMgr::onEquipDisplayFlagsChanged( Entity::Player& player )
{
auto paramPacket = makeZonePacket< FFXIVIpcConfig >( player.getId() );
paramPacket->data().flag = player.getEquipDisplayFlags();
server().queueForPlayers( player.getInRangePlayerIds( true ), paramPacket );
}
2023-02-20 15:25:57 +01:00
void PlayerMgr::onConditionChanged( Entity::Player& player, bool updateInRange )
{
2023-02-20 15:25:57 +01:00
server().queueForPlayer( player.getCharacterId(), std::make_shared< ConditionPacket >( player ) );
if( updateInRange )
server().queueForPlayers( player.getInRangePlayerIds( true ),
makeActorControl( player.getId(), SetStatusIcon, static_cast< uint8_t >( player.getOnlineStatus() ) ) );
}
2023-02-20 15:25:57 +01:00
void PlayerMgr::onAchievementListChanged( Entity::Player& player )
{
2023-01-27 18:54:54 -03:00
auto achvData = player.getAchievementData();
auto achvPacket = makeZonePacket< FFXIVIpcAchievement >( player.getId() );
2023-01-27 18:54:54 -03:00
std::memcpy( &achvPacket->data().complete[ 0 ], &achvData.unlockList[ 0 ], sizeof( achvPacket->data().complete ) );
std::memcpy( &achvPacket->data().history[ 0 ], &achvData.history[ 0 ], sizeof( achvPacket->data().history ) );
server().queueForPlayer( player.getCharacterId(), achvPacket );
}
2023-02-20 15:25:57 +01:00
void PlayerMgr::onAchievementProgressChanged( Entity::Player& player, uint32_t achievementId )
{
auto& achvMgr = Common::Service< Manager::AchievementMgr >::ref();
auto achvProgress = achvMgr.getAchievementDataById( player, achievementId );
auto pAchvProgressPacket = makeActorControl( player.getId(), AchievementSetRate, achievementId, achvProgress.first, achvProgress.second );
server().queueForPlayer( player.getCharacterId(), pAchvProgressPacket );
}
void PlayerMgr::onUnlockAchievement( Entity::Player& player, uint32_t achievementId )
{
2023-02-20 15:25:57 +01:00
onAchievementListChanged( player );
server().queueForPlayer( player.getCharacterId(), makeActorControl( player.getId(), AchievementComplete, achievementId ) );
server().queueForPlayer( player.getCharacterId(), makeActorControl( player.getId(), AchievementObtainMsg, achievementId ) );
}
2023-02-20 15:25:57 +01:00
void PlayerMgr::onStatsChanged( Entity::Player& player )
{
2023-02-20 15:25:57 +01:00
std::array< uint32_t, 50 > statParams{};
std::fill( std::begin( statParams ), std::end( statParams ), 0 );
auto& exd = Common::Service< Data::ExdData >::ref();
// todo: this is no doubt slow as shit...
2022-01-27 21:24:54 +01:00
auto idList = exd.getIdList< Excel::BaseParam >();
for( const auto id : idList )
{
2022-01-27 21:24:54 +01:00
auto row = exd.getRow< Excel::BaseParam >( id );
if( !row )
{
continue;
}
if( row->data().PacketIndex < 0 )
{
continue;
}
statParams[ row->data().PacketIndex ] = player.getStatValue( static_cast< Common::BaseParam >( id ) );
}
auto statPacket = makeZonePacket< FFXIVIpcBaseParam >( player.getId() );
memcpy( statPacket->data().Param, statParams.data(), sizeof( uint32_t ) * statParams.size() );
server().queueForPlayer( player.getCharacterId(), statPacket );
}
2023-02-25 15:31:57 +01:00
void PlayerMgr::sendStatusUpdate( Entity::Player& player )
{
auto playerStatusUpdate = makeZonePacket< FFXIVIpcPlayerStatusUpdate >( player.getId() );
playerStatusUpdate->data().ClassJob = static_cast< uint8_t >( player.getClass() );
playerStatusUpdate->data().Lv = player.getLevel();
2023-01-19 09:57:08 +01:00
playerStatusUpdate->data().Lv1 = player.getLevel();
playerStatusUpdate->data().LvSync = 0; //player.getLevelSync();
playerStatusUpdate->data().Exp = player.getExp();
server().queueForPlayer( player.getCharacterId(), playerStatusUpdate );
}
2023-02-25 15:31:57 +01:00
void PlayerMgr::onHudParamChanged( Entity::Player& player )
{
auto hudParamPacket = makeHudParam( player );
server().queueForPlayer( player.getCharacterId(), hudParamPacket );
}
2023-02-25 15:31:57 +01:00
void PlayerMgr::onRestingTick( Entity::Player& player )
{
server().queueForPlayers( player.getInRangePlayerIds( true ), std::make_shared< RestingPacket >( player ) );
}
void PlayerMgr::sendItemLevel( Entity::Player& player )
{
server().queueForPlayer( player.getCharacterId(), makeActorControl( player.getId(), SetItemLevel, player.getItemLevel(), 0 ) );
}
void PlayerMgr::onLevelUp( Entity::Player& player )
{
player.calculateStats();
player.sendStats();
2023-02-25 15:31:57 +01:00
player.sendHudParam();
auto inRangePlayerIds = player.getInRangePlayerIds( true );
server().queueForPlayers( inRangePlayerIds, makeHudParam( player ) );
server().queueForPlayers( inRangePlayerIds, makeActorControl( player.getId(), LevelUpEffect, static_cast< uint8_t >( player.getClass() ),
2023-02-25 15:31:57 +01:00
player.getLevel(), player.getLevel() - 1 ) );
2023-02-25 15:31:57 +01:00
sendStatusUpdate( player );
auto& achvMgr = Common::Service< World::Manager::AchievementMgr >::ref();
achvMgr.progressAchievementByType< Common::Achievement::Type::Classjob >( player, static_cast< uint32_t >( player.getClass() ) );
}
void PlayerMgr::onSetLevelForClass( Entity::Player& player, Common::ClassJob classJob )
{
auto& achvMgr = Common::Service< World::Manager::AchievementMgr >::ref();
server().queueForPlayer( player.getCharacterId(), makeActorControlSelf( player.getId(), Network::ActorControl::ClassJobUpdate,
2023-02-20 15:25:57 +01:00
static_cast< uint8_t >( classJob ), player.getLevelForClass( classJob ) ) );
achvMgr.progressAchievementByType< Common::Achievement::Type::Classjob >( player, static_cast< uint32_t >( classJob ) );
}
void PlayerMgr::onGainExp( Entity::Player& player, uint32_t exp )
{
if( exp != 0 )
2023-02-20 15:25:57 +01:00
server().queueForPlayer( player.getCharacterId(), makeActorControlSelf( player.getId(), GainExpMsg,
static_cast< uint8_t >( player.getClass() ), exp ) );
2023-02-20 15:25:57 +01:00
server().queueForPlayer( player.getCharacterId(), makeActorControlSelf( player.getId(), UpdateUiExp,
static_cast< uint8_t >( player.getClass() ), player.getExp() ) );
}
void PlayerMgr::onUnlockOrchestrion( Entity::Player& player, uint8_t songId, uint32_t itemId )
{
player.learnSong( songId, itemId );
server().queueForPlayer( player.getCharacterId(), makeActorControlSelf( player.getId(), ToggleOrchestrionUnlock, songId, 1, itemId ) );
}
2023-02-20 15:25:57 +01:00
void PlayerMgr::onGearChanged( Entity::Player& player )
{
server().queueForPlayers( player.getInRangePlayerIds( true ), std::make_shared< ModelEquipPacket >( player ) );
}
2023-02-25 15:31:57 +01:00
void PlayerMgr::sendGrandCompany( Entity::Player& player )
{
2023-01-22 21:53:27 +01:00
auto gcAffPacket = makeZonePacket< FFXIVIpcGrandCompany >( player.getId() );
gcAffPacket->data().ActiveCompanyId = player.getGc();
gcAffPacket->data().MaelstromRank = player.getGcRankArray()[ 0 ];
gcAffPacket->data().TwinAdderRank = player.getGcRankArray()[ 1 ];
gcAffPacket->data().ImmortalFlamesRank = player.getGcRankArray()[ 2 ];
server().queueForPlayer( player.getCharacterId(), gcAffPacket );
}
2023-02-20 15:25:57 +01:00
void PlayerMgr::setGrandCompany( Entity::Player& player, uint8_t gc )
{
player.setGc( gc );
2023-02-25 15:31:57 +01:00
sendGrandCompany( player );
}
2023-02-20 15:25:57 +01:00
void PlayerMgr::setGrandCompanyRank( Entity::Player& player, uint8_t gc, uint8_t rank )
{
player.setGcRankAt( gc, rank );
2023-02-25 15:31:57 +01:00
sendGrandCompany( player );
}
2023-01-23 21:39:12 +01:00
void PlayerMgr::onCompanionUpdate( Entity::Player& player, uint8_t companionId )
{
auto& exdData = Common::Service< Data::ExdData >::ref();
auto companion = exdData.getRow< Excel::Companion >( companionId );
if( !companion )
return;
player.setCompanion( companionId );
server().queueForPlayers( player.getInRangePlayerIds( true ), makeActorControl( player.getId(), ActorControlType::ToggleCompanion, companionId ) );
2023-01-23 21:39:12 +01:00
}
void PlayerMgr::onMountUpdate( Entity::Player& player, uint32_t mountId )
{
auto inRangePlayerIds = player.getInRangePlayerIds( true );
if( mountId != 0 )
{
server().queueForPlayers( inRangePlayerIds,
2023-02-25 15:31:57 +01:00
makeActorControl( player.getId(), ActorControlType::SetStatus, static_cast< uint8_t >( Common::ActorStatus::Mounted ) ) );
server().queueForPlayers( inRangePlayerIds, makeActorControlSelf( player.getId(), 0x39e, 12 ) );
}
else
{
server().queueForPlayers( inRangePlayerIds,
2023-02-25 15:31:57 +01:00
makeActorControl( player.getId(), ActorControlType::SetStatus, static_cast< uint8_t >( Common::ActorStatus::Idle ) ) );
server().queueForPlayers( inRangePlayerIds, makeActorControlSelf( player.getId(), ActorControlType::Dismount, 1 ) );
}
auto mountPacket = makeZonePacket< FFXIVIpcMount >( player.getId() );
mountPacket->data().id = mountId;
server().queueForPlayers( inRangePlayerIds, mountPacket );
}
2022-02-23 08:36:23 +01:00
void PlayerMgr::onMobKill( Entity::Player& player, Entity::BNpc& bnpc )
{
auto& scriptMgr = Common::Service< Scripting::ScriptMgr >::ref();
2022-02-23 08:36:23 +01:00
scriptMgr.onBNpcKill( player, bnpc );
2022-02-10 18:50:44 +01:00
if( player.hasReward( Common::UnlockEntry::HuntingLog ) )
{
2022-02-23 08:36:23 +01:00
player.updateHuntingLog( bnpc.getBNpcNameId() );
}
}
void PlayerMgr::onHateListChanged( Entity::Player& player )
{
auto& teriMgr = Common::Service< World::Manager::TerritoryMgr >::ref();
auto hateListPacket = makeZonePacket< FFXIVIpcHateList >( player.getId() );
auto hateRankPacket = makeZonePacket< FFXIVIpcHaterList >( player.getId() );
auto actorIdToHateSlotMap = player.getActorIdToHateSlotMap();
hateListPacket->data().Count = static_cast< uint8_t >( actorIdToHateSlotMap.size() );
hateRankPacket->data().Count = static_cast< uint8_t >( actorIdToHateSlotMap.size() );
auto it = actorIdToHateSlotMap.begin();
auto zone = teriMgr.getTerritoryByGuId( player.getTerritoryId() );
if( !zone )
return;
for( int32_t i = 0; it != actorIdToHateSlotMap.end(); ++it, ++i )
{
auto pBNpc = zone->getActiveBNpcByEntityId( it->first );
if( !pBNpc )
continue;
auto hateValue = pBNpc->hateListGetValue( player.getAsChara() );
if( hateValue == 0 )
continue;
auto hatePercent = ( hateValue / static_cast< float >( pBNpc->hateListGetHighestValue() ) ) * 100.f;
hateListPacket->data().List[ i ].Id = player.getId();
hateListPacket->data().List[ i ].Value = hateValue;
hateRankPacket->data().List[ i ].Id = it->first;
hateRankPacket->data().List[ i ].Rate = static_cast< uint8_t >( hatePercent );
}
server().queueForPlayer( player.getCharacterId(), { hateListPacket, hateRankPacket } );
}
2023-02-20 15:25:57 +01:00
void PlayerMgr::onClassChanged( Entity::Player& player )
{
server().queueForPlayers( player.getInRangePlayerIds( true ), makeActorControl( player.getId(), ClassJobChange, 0x04 ) );
2023-02-25 15:31:57 +01:00
onHudParamChanged( player );
}
void PlayerMgr::sendLoginMessage( Entity::Player& player )
{
auto motd = server().getConfig().motd;
std::istringstream ss( motd );
std::string msg;
while( std::getline( ss, msg, ';' ) )
{
2023-01-27 11:13:57 +01:00
sendServerNotice( player, msg );
}
}
void PlayerMgr::onLogin( Entity::Player &player )
{
}
2023-02-10 21:38:08 +01:00
void PlayerMgr::onLogout( Entity::Player &player )
{
}
void PlayerMgr::onDeath( Entity::Player& player )
{
auto& scriptMgr = Common::Service< Scripting::ScriptMgr >::ref();
scriptMgr.onPlayerDeath( player );
}
2023-02-20 15:25:57 +01:00
void PlayerMgr::onMoveZone( Sapphire::Entity::Player& player )
2023-01-27 11:13:57 +01:00
{
auto& teriMgr = Common::Service< World::Manager::TerritoryMgr >::ref();
auto& housingMgr = Common::Service< HousingMgr >::ref();
auto& partyMgr = Common::Service< World::Manager::PartyMgr >::ref();
auto pZone = teriMgr.getTerritoryByGuId( player.getTerritoryId() );
if( !pZone )
{
Logger::error( "Territory GuID#{} not found!", player.getTerritoryId() );
return;
}
auto& teri = *pZone;
auto initPacket = makeZonePacket< FFXIVIpcLogin >( player.getId() );
initPacket->data().playerActorId = player.getId();
server().queueForPlayer( player.getCharacterId(), initPacket );
2023-01-27 11:13:57 +01:00
player.sendInventory();
if( player.isLogin() )
{
server().queueForPlayer( player.getCharacterId(), makeActorControlSelf( player.getId(), SetConfigFlags, player.getEquipDisplayFlags(), 1 ) );
server().queueForPlayer( player.getCharacterId(), makeActorControlSelf( player.getId(), SetMaxGearSets, player.getMaxGearSets() ) );
2023-01-27 11:13:57 +01:00
}
// set flags, will be reset automatically by zoning ( only on client side though )
//setStateFlag( PlayerStateFlag::BetweenAreas );
//setStateFlag( PlayerStateFlag::BetweenAreas1 );
player.sendHuntingLog();
2023-01-27 11:13:57 +01:00
if( player.isLogin() )
server().queueForPlayer( player.getCharacterId(), makePlayerSetup( player ) );
player.sendRecastGroups();
player.sendStats();
2023-02-25 15:31:57 +01:00
sendItemLevel( player );
2023-01-27 11:13:57 +01:00
if( player.isLogin() )
{
auto classInfo = makeZonePacket< FFXIVIpcChangeClass >( player.getId() );
classInfo->data().ClassJob = static_cast< uint8_t >( player.getClass() );
classInfo->data().Lv = player.getLevel();
classInfo->data().Lv1 = player.getLevel();
if( player.isLogin() )
classInfo->data().Login = 1;
server().queueForPlayer( player.getCharacterId(), classInfo );
server().queueForPlayer( player.getCharacterId(), makeActorControl( player.getId(), 0x112, 0x24 ) ); // unknown
2023-01-27 11:13:57 +01:00
auto contentFinderList = makeZonePacket< FFXIVIpcContentAttainFlags >( player.getId() );
std::memset( &contentFinderList->data(), 0xFF, sizeof( contentFinderList->data() ) );
server().queueForPlayer( player.getCharacterId(), contentFinderList );
2023-01-27 11:13:57 +01:00
player.clearSoldItems();
}
if( Sapphire::LandPtr pLand = housingMgr.getLandByOwnerId( player.getCharacterId() ) )
{
uint32_t state = 0;
if( pLand->getHouse() )
{
state |= Common::LandFlags::CHARA_HOUSING_LAND_DATA_FLAG_HOUSE;
// todo: remove this, debug for now
state |= Common::LandFlags::CHARA_HOUSING_LAND_DATA_FLAG_AETHERYTE;
}
player.setLandFlags( Common::LandFlagsSlot::Private, state, pLand->getLandIdent() );
}
housingMgr.sendLandFlags( player );
server().queueForPlayer( player.getCharacterId(), makeInitZone( player, teri ) );
2023-01-27 11:13:57 +01:00
teri.onPlayerZoneIn( player );
if( player.isLogin() )
{
server().queueForPlayer( player.getCharacterId(),
{
makeZonePacket< FFXIVIpcDailyQuests >( player.getId() ),
makeZonePacket< FFXIVIpcQuestRepeatFlags >( player.getId() )
} );
auto &questMgr = Common::Service< World::Manager::QuestMgr >::ref();
questMgr.sendQuestsInfo( player );
2023-02-25 15:31:57 +01:00
sendGrandCompany( player );
2023-01-27 11:13:57 +01:00
}
}
void PlayerMgr::onUpdate( Entity::Player& player, uint64_t tickCount )
{
if( player.getHp() <= 0 && player.getStatus() != Common::ActorStatus::Dead )
{
player.die();
onDeath( player );
}
if( !player.isAlive() )
return;
2023-02-25 15:31:57 +01:00
auto mainWeap = player.getItemAt( Common::GearSet0, Common::GearSetSlot::MainHand );
if( mainWeap && !player.checkAction() && ( player.getTargetId() && player.getStance() == Common::Stance::Active && player.isAutoattackOn() ) )
{
// @TODO i dislike this, iterating over all in range actors when you already know the id of the actor you need...
for( const auto& actor : player.getInRangeActors() )
{
2023-02-25 15:31:57 +01:00
if( actor->getId() != player.getTargetId() || !actor->getAsChara()->isAlive() )
continue;
auto chara = actor->getAsChara();
2023-02-25 15:31:57 +01:00
// default autoattack range
float range = 3.f + chara->getRadius() + player.getRadius() * 0.5f;
2023-02-25 15:31:57 +01:00
// default autoattack range for ranged classes
auto classJob = player.getClass();
2023-02-25 15:31:57 +01:00
if( classJob == Common::ClassJob::Machinist || classJob == Common::ClassJob::Bard || classJob == Common::ClassJob::Archer )
range = 25.f + chara->getRadius() + player.getRadius() * 0.5f;
2023-02-25 15:31:57 +01:00
if( Common::Util::distance( player.getPos(), actor->getPos() ) <= range )
{
if( ( tickCount - player.getLastAttack() ) > mainWeap->getDelay() )
{
2023-02-25 15:31:57 +01:00
player.setLastAttack( tickCount );
player.autoAttack( actor->getAsChara() );
}
}
}
}
}
2023-02-20 15:25:57 +01:00
void PlayerMgr::setCondition( Sapphire::Entity::Player& player, Common::PlayerCondition flag )
{
auto prevOnlineStatus = player.getOnlineStatus();
2023-02-20 15:25:57 +01:00
player.setCondition( flag );
auto newOnlineStatus = player.getOnlineStatus();
2023-02-20 15:25:57 +01:00
onConditionChanged( player, prevOnlineStatus != newOnlineStatus );
}
2023-02-20 15:25:57 +01:00
void PlayerMgr::removeCondition( Sapphire::Entity::Player& player, Common::PlayerCondition flag )
{
auto prevOnlineStatus = player.getOnlineStatus();
2023-02-20 15:25:57 +01:00
player.removeCondition( flag );
auto newOnlineStatus = player.getOnlineStatus();
2023-02-20 15:25:57 +01:00
onConditionChanged( player, prevOnlineStatus != newOnlineStatus );
}
////////// Helper ///////////
void PlayerMgr::sendServerNotice( Entity::Player& player, const std::string& message ) //Purple Text
{
server().queueForPlayer( player.getCharacterId(), std::make_shared< ServerNoticePacket >( player.getId(), message ) );
}
void PlayerMgr::sendUrgent( Entity::Player& player, const std::string& message ) //Red Text
{
server().queueForPlayer( player.getCharacterId(), std::make_shared< ChatPacket >( player, Common::ChatType::ServerUrgent, message ) );
}
void PlayerMgr::sendDebug( Entity::Player& player, const std::string& message ) //Grey Text
{
server().queueForPlayer( player.getCharacterId(), std::make_shared< ChatPacket >( player, Common::ChatType::SystemMessage, message ) );
}
void PlayerMgr::sendLogMessage( Entity::Player& player, uint32_t messageId, uint32_t param2, uint32_t param3,
uint32_t param4, uint32_t param5, uint32_t param6 )
{
server().queueForPlayer( player.getCharacterId(), makeActorControlTarget( player.getId(), ActorControlType::LogMsg, messageId,
param2, param3, param4, param5, param6 ) );
}