diff --git a/sql/charadetail.sql b/sql/charadetail.sql index c8430552..923662fc 100644 --- a/sql/charadetail.sql +++ b/sql/charadetail.sql @@ -77,6 +77,7 @@ CREATE TABLE charadetail ( `ContentRetryTime` blob, `ContentJoinTime` int(10) DEFAULT NULL, `ContentClearFlag` blob, + `CFPenaltyUntil` int unsigned NOT NULL DEFAULT '0', `TownWarpFstFlags` binary(2) DEFAULT NULL, `PathId` int(10) DEFAULT NULL, `StepIndex` int(5) DEFAULT NULL, diff --git a/src/libraries b/src/libraries deleted file mode 160000 index 23b9c0a1..00000000 --- a/src/libraries +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 23b9c0a154a327b25d700cd6857a4e53bed2e8a1 diff --git a/src/servers/Server_Common/Common.h b/src/servers/Server_Common/Common.h index 37520915..56ab29f9 100644 --- a/src/servers/Server_Common/Common.h +++ b/src/servers/Server_Common/Common.h @@ -265,7 +265,7 @@ namespace Core { JOB_ASTROLOGIAN = 33, // astro JOB_SAMURAI = 34, // sam JOB_REDMAGE = 35, // red mage - + }; enum PlayerSyncFlags : uint32_t @@ -285,13 +285,15 @@ namespace Core { HpMp = 0x00000800, QuestTracker = 0x00001000, NewGame = 0x00002000, - + // 0x0000400 is missing here Unlocks = 0x00008000, PlayTime = 0x00010000, NewAdventurer = 0x00020000, SearchInfo = 0x00040000, GC = 0x00080000, + CFPenaltyTime = 0x00100000, + All = 0xFFFFFFFF, }; diff --git a/src/servers/Server_Common/Util.cpp b/src/servers/Server_Common/Util.cpp index 2d7a6a41..ec40f38b 100644 --- a/src/servers/Server_Common/Util.cpp +++ b/src/servers/Server_Common/Util.cpp @@ -25,9 +25,8 @@ uint64_t Core::Util::getTimeMs() uint64_t Core::Util::getTimeSeconds() { - std::chrono::high_resolution_clock::time_point t1 = std::chrono::high_resolution_clock::now(); - auto now = std::chrono::time_point_cast< std::chrono::seconds >( t1 ).time_since_epoch().count(); - return now; + std::chrono::seconds epoch = std::chrono::duration_cast< std::chrono::seconds >(std::chrono::system_clock::now().time_since_epoch()); + return epoch.count(); } uint64_t Core::Util::getEorzeanTimeStamp() diff --git a/src/servers/Server_Zone/ContentFinder.cpp b/src/servers/Server_Zone/ContentFinder.cpp new file mode 100644 index 00000000..e69de29b diff --git a/src/servers/Server_Zone/ContentFinder.h b/src/servers/Server_Zone/ContentFinder.h new file mode 100644 index 00000000..9c7a33e6 --- /dev/null +++ b/src/servers/Server_Zone/ContentFinder.h @@ -0,0 +1,12 @@ +#ifndef _CONTENTFINDER_H +#define _CONTENTFINDER_H + +namespace Core { + class ContentFinder { + private: + + + }; +} + +#endif \ No newline at end of file diff --git a/src/servers/Server_Zone/GameCommandHandler.cpp b/src/servers/Server_Zone/GameCommandHandler.cpp index 37aa0224..ad26d655 100644 --- a/src/servers/Server_Zone/GameCommandHandler.cpp +++ b/src/servers/Server_Zone/GameCommandHandler.cpp @@ -79,7 +79,7 @@ void Core::GameCommandHandler::execCommand( char * data, Core::Entity::PlayerPtr // no parameters, just get the command commandString = tmpCommand; - // try to retrieve the command + // try to retrieve the command auto it = m_commandMap.find( commandString ); if( it == m_commandMap.end() ) @@ -237,7 +237,6 @@ void Core::GameCommandHandler::set( char * data, Core::Entity::PlayerPtr pPlayer g_database.execute( query2.c_str() ); } - else if( subCommand == "discovery_reset" ) { pPlayer->resetDiscovery(); @@ -257,6 +256,13 @@ void Core::GameCommandHandler::set( char * data, Core::Entity::PlayerPtr pPlayer else pPlayer->setClassJob( static_cast ( id ) ); } + else if( subCommand == "cfpenalty") + { + uint32_t minutes; + sscanf( params.c_str(), "%d", &minutes ); + + pPlayer->setPenaltyMinutes( minutes ); + } } @@ -411,7 +417,7 @@ void Core::GameCommandHandler::add( char * data, Core::Entity::PlayerPtr pPlayer sscanf( params.c_str(), "%x %x %x %x %x %x %x %x", &opcode, ¶m1, ¶m2, ¶m3, ¶m4, ¶m5, ¶m6, &playerId ); pPlayer->sendNotice( "Injecting ACTOR_CONTROL " + std::to_string( opcode ) ); - + Network::Packets::GamePacketNew< Network::Packets::Server::FFXIVIpcActorControl143 > actorControl( playerId, pPlayer->getId() ); actorControl.data().category = opcode; actorControl.data().param1 = param1; diff --git a/src/servers/Server_Zone/PacketHandlers.cpp b/src/servers/Server_Zone/PacketHandlers.cpp index 47694e47..35661060 100644 --- a/src/servers/Server_Zone/PacketHandlers.cpp +++ b/src/servers/Server_Zone/PacketHandlers.cpp @@ -164,7 +164,7 @@ void Core::Network::GameConnection::gm1Handler( Core::Network::Packets::GamePack g_log.debug( pPlayer->getName() + " used GM1 commandId: " + std::to_string( commandId ) + ", params: " + std::to_string( param1 ) + ", " + std::to_string( param2 ) + ", " + std::to_string( param3 ) ); Core::Entity::ActorPtr targetActor; - + if( pPlayer->getId() == param3 ) { @@ -311,23 +311,23 @@ void Core::Network::GameConnection::gm1Handler( Core::Network::Packets::GamePack case GmCommand::Weather: { targetPlayer->getCurrentZone()->setWeatherOverride( param1 ); - pPlayer->sendNotice( "Weather in Zone \"" + targetPlayer->getCurrentZone()->getName() + "\" of " + + pPlayer->sendNotice( "Weather in Zone \"" + targetPlayer->getCurrentZone()->getName() + "\" of " + targetPlayer->getName() + " set in range." ); break; } case GmCommand::TeriInfo: { - pPlayer->sendNotice( "ZoneId: " + std::to_string( pPlayer->getZoneId() ) + "\nName: " + - pPlayer->getCurrentZone()->getName() + "\nInternalName: " + - pPlayer->getCurrentZone()->getInternalName() + "\nPopCount: " + - std::to_string( pPlayer->getCurrentZone()->getPopCount() ) + + pPlayer->sendNotice( "ZoneId: " + std::to_string( pPlayer->getZoneId() ) + "\nName: " + + pPlayer->getCurrentZone()->getName() + "\nInternalName: " + + pPlayer->getCurrentZone()->getInternalName() + "\nPopCount: " + + std::to_string( pPlayer->getCurrentZone()->getPopCount() ) + "\nCurrentWeather:" + std::to_string( pPlayer->getCurrentZone()->getCurrentWeather() ) + "\nNextWeather:" + std::to_string( pPlayer->getCurrentZone()->getNextWeather() ) ); break; } case GmCommand::Jump: { - + auto inRange = pPlayer->getInRangeActors(); for( auto actor : inRange ) { @@ -340,7 +340,7 @@ void Core::Network::GameConnection::gm1Handler( Core::Network::Packets::GamePack case GmCommand::Collect: { uint32_t gil = targetPlayer->getCurrency( 1 ); - + if( gil < param1 ) { pPlayer->sendUrgent( "Player does not have enough Gil(" + std::to_string( gil ) + ")" ); @@ -418,7 +418,7 @@ void Core::Network::GameConnection::gm2Handler( Core::Network::Packets::GamePack { targetActor = pPlayer; } - else + else { pPlayer->sendUrgent("Player " + param1 + " not found on this server."); return; @@ -468,12 +468,12 @@ void Core::Network::GameConnection::gm2Handler( Core::Network::Packets::GamePack case GmCommand::Inspect: { pPlayer->sendNotice( "Name: " + targetPlayer->getName() + - "\nGil: " + std::to_string( targetPlayer->getCurrency( 1 ) ) + - "\nZone: " + targetPlayer->getCurrentZone()->getName() + + "\nGil: " + std::to_string( targetPlayer->getCurrency( 1 ) ) + + "\nZone: " + targetPlayer->getCurrentZone()->getName() + "(" + std::to_string( targetPlayer->getZoneId() ) + ")" + - "\nClass: " + std::to_string( targetPlayer->getClass() ) + - "\nLevel: " + std::to_string( targetPlayer->getLevel() ) + - "\nExp: " + std::to_string( targetPlayer->getExp() ) + + "\nClass: " + std::to_string( targetPlayer->getClass() ) + + "\nLevel: " + std::to_string( targetPlayer->getLevel() ) + + "\nExp: " + std::to_string( targetPlayer->getExp() ) + "\nSearchMessage: " + targetPlayer->getSearchMessage() + "\nPlayTime: " + std::to_string( targetPlayer->getPlayTime() ) ); break; @@ -751,7 +751,7 @@ void Core::Network::GameConnection::inventoryModifyHandler( Core::Network::Packe ackPacket.data().sequence = seq; ackPacket.data().type = 7; pPlayer->queuePacket( ackPacket ); - + g_log.debug( pInPacket->toString() ); g_log.debug( "InventoryAction: " + std::to_string( action ) ); @@ -881,10 +881,10 @@ void Core::Network::GameConnection::actionHandler( Core::Network::Packets::GameP { switch( pPlayer->getZoningType() ) { - case ZoneingType::None: - pPlayer->sendToInRangeSet( ActorControlPacket143( pPlayer->getId(), ZoneIn, 0x01 ), true ); + case ZoneingType::None: + pPlayer->sendToInRangeSet( ActorControlPacket143( pPlayer->getId(), ZoneIn, 0x01 ), true ); break; - case ZoneingType::Teleport: + case ZoneingType::Teleport: pPlayer->sendToInRangeSet( ActorControlPacket143( pPlayer->getId(), ZoneIn, 0x01, 0, 0, 110 ), true ); break; case ZoneingType::Return: @@ -905,10 +905,10 @@ void Core::Network::GameConnection::actionHandler( Core::Network::Packets::GameP break; case ZoneingType::FadeIn: break; - default: + default: break; } - + pPlayer->setZoningType( Common::ZoneingType::None ); pPlayer->unsetStateFlag( PlayerStateFlag::BetweenAreas ); @@ -921,7 +921,7 @@ void Core::Network::GameConnection::actionHandler( Core::Network::Packets::GameP { // TODO: only register this action if enough gil is in possession auto targetAetheryte = g_exdData.getAetheryteInfo( param11 ); - + if( targetAetheryte ) { auto fromAetheryte = g_exdData.getAetheryteInfo( g_exdData.m_zoneInfoMap[pPlayer->getZoneId()].aetheryte_index ); @@ -1170,6 +1170,15 @@ void Core::Network::GameConnection::cfDutyInfoRequest(Core::Network::Packets::Ga Core::Entity::PlayerPtr pPlayer) { GamePacketNew< FFXIVIpcCFDutyInfo > dutyInfoPacket( pPlayer->getId() ); + + auto penaltyMinutes = pPlayer->getPenaltyMinutes(); + if (penaltyMinutes > 255) + { + // cap it since it's uint8_t in packets + penaltyMinutes = 255; + } + + dutyInfoPacket.data().penaltyTime = penaltyMinutes; queueOutPacket( dutyInfoPacket ); GamePacketNew< FFXIVIpcCFPlayerInNeed > inNeedsPacket( pPlayer->getId() ); diff --git a/src/servers/Server_Zone/Player.cpp b/src/servers/Server_Zone/Player.cpp index 5ccd7e85..5d8f6aba 100644 --- a/src/servers/Server_Zone/Player.cpp +++ b/src/servers/Server_Zone/Player.cpp @@ -86,6 +86,9 @@ Core::Entity::Player::Player() : memset( m_name, 0, sizeof( m_name ) ); memset( m_stateFlags, 0, sizeof( m_stateFlags ) ); memset( m_searchMessage, 0, sizeof( m_searchMessage ) ); + + // content finder + m_cfPenaltyUntil = 0; } Core::Entity::Player::~Player() @@ -190,7 +193,7 @@ void Core::Entity::Player::calculateStats() auto paramGrowthInfoIt = g_exdData.m_paramGrowthInfoMap.find( level ); if( tribeInfoIt == g_exdData.m_tribeInfoMap.end() || - classInfoIt == g_exdData.m_classJobInfoMap.end() || + classInfoIt == g_exdData.m_classJobInfoMap.end() || paramGrowthInfoIt == g_exdData.m_paramGrowthInfoMap.end() ) return; @@ -200,7 +203,7 @@ void Core::Entity::Player::calculateStats() // TODO: put formula somewhere else... float base = 0.0f; - + if( level < 51 ) base = 0.053f * ( level * level ) + ( 1.022f * level ) - 0.907f + 20; else @@ -220,11 +223,11 @@ void Core::Entity::Player::calculateStats() m_baseStats.attackPotMagic = paramGrowthInfo.base_secondary; m_baseStats.healingPotMagic = paramGrowthInfo.base_secondary; - m_baseStats.max_mp = floor( - floor( - ( ( m_baseStats.pie - base ) * ( static_cast< float >( paramGrowthInfo.piety_scalar ) / 100 ) ) + paramGrowthInfo.mp_const ) * ( static_cast< float >( classInfo.mod_mpcpgp ) / 100 ) + m_baseStats.max_mp = floor( + floor( + ( ( m_baseStats.pie - base ) * ( static_cast< float >( paramGrowthInfo.piety_scalar ) / 100 ) ) + paramGrowthInfo.mp_const ) * ( static_cast< float >( classInfo.mod_mpcpgp ) / 100 ) ); - + m_baseStats.max_hp = floor( floor( ( ( m_baseStats.vit - base ) * ( ( static_cast< float >( paramGrowthInfo.piety_scalar ) ) / 100 ) ) + paramGrowthInfo.hp_mod ) * ( static_cast< float >( classInfo.mod_hp * 0.9f ) / 100 ) * 15 @@ -305,7 +308,7 @@ void Core::Entity::Player::teleport( uint16_t aetheryteId, uint8_t type ) pos = z_pos->getTargetPosition(); rot = z_pos->getTargetRotation(); } - + sendDebug( "Teleport: " + data->placename + " " + data->placename_aethernet + "(" + std::to_string( data->levelId ) + ")" ); @@ -380,7 +383,7 @@ void Core::Entity::Player::setZone( uint32_t zoneId ) sendInventory(); - // set flags, will be reset automatically by zoning ( only on client side though ) + // set flags, will be reset automatically by zoning ( only on client side though ) pPlayer->setStateFlag( PlayerStateFlag::BetweenAreas ); pPlayer->setStateFlag( PlayerStateFlag::BetweenAreas1 ); pPlayer->sendStateFlags(); @@ -518,7 +521,7 @@ void Core::Entity::Player::discover( int16_t map_id, int16_t sub_id ) // map.exd field 12 -> index in one of the two discovery sections, if field 15 is false, need to use 2nd section // section 1 starts at 4 - 2 bytes each - // section to starts at 320 - 4 bytes long + // section to starts at 320 - 4 bytes long int32_t offset = 4; @@ -810,7 +813,7 @@ void Core::Entity::Player::eventActionStart( uint32_t eventId, setCurrentAction( pEventAction ); auto pEvent = getEvent( eventId ); - + if( !pEvent && getEventCount() ) { // We're trying to play a nested event, need to start it first. @@ -1096,7 +1099,7 @@ void Core::Entity::Player::update( int64_t currTime ) for( auto actor : m_inRangeActors ) { - if( isAutoattackOn() && + if( isAutoattackOn() && actor->getId() == m_targetId && actor->isAlive() && mainWeap && @@ -1452,7 +1455,7 @@ void Core::Entity::Player::autoAttack( ActorPtr pTarget ) //uint64_t tick = Util::getTimeMs(); //srand(static_cast< uint32_t >(tick)); - uint32_t damage = mainWeap->getAutoAttackDmg() + rand() % 12; + uint32_t damage = mainWeap->getAutoAttackDmg() + rand() % 12; uint32_t variation = 0 + rand() % 3; if( getClass() == 5 || getClass() == 23 || getClass() == 31 ) @@ -1497,5 +1500,41 @@ void Core::Entity::Player::autoAttack( ActorPtr pTarget ) } pTarget->takeDamage(damage); - + } + +///////////////////////////// +// Content Finder +///////////////////////////// +uint32_t Core::Entity::Player::getPenaltyTimestamp() const +{ + return m_cfPenaltyUntil; +} + +void Core::Entity::Player::setPenaltyTimestamp( uint32_t timestamp ) +{ + m_cfPenaltyUntil = timestamp; + setSyncFlag( PlayerSyncFlags::CFPenaltyTime ); +} + +uint32_t Core::Entity::Player::getPenaltyMinutes() const +{ + auto currentTimestamp = Core::Util::getTimeSeconds(); + auto endTimestamp = getPenaltyTimestamp(); + + // check if penalty timestamp already passed current time + if (currentTimestamp > endTimestamp) + { + return 0; + } + + auto deltaTime = endTimestamp - currentTimestamp; + return ceil(static_cast< float > (deltaTime) / 60); +} + +void Core::Entity::Player::setPenaltyMinutes( uint32_t minutes ) +{ + auto currentTimestamp = Core::Util::getTimeSeconds(); + setPenaltyTimestamp(currentTimestamp + minutes * 60); +} + diff --git a/src/servers/Server_Zone/Player.h b/src/servers/Server_Zone/Player.h index ae5af385..1a0a45cb 100644 --- a/src/servers/Server_Zone/Player.h +++ b/src/servers/Server_Zone/Player.h @@ -78,7 +78,7 @@ public: void checkEvent( uint32_t eventId ); - + // Events ////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -482,6 +482,14 @@ public: void setAutoattack( bool mode ); bool isAutoattackOn() const; + // Player Content Finder Handling + ////////////////////////////////////////////////////////////////////////////////////////////////////// + uint32_t getPenaltyTimestamp() const; + void setPenaltyTimestamp( uint32_t timestamp ); + + uint32_t getPenaltyMinutes() const; + void setPenaltyMinutes( uint32_t minutes ); + private: uint32_t m_lastWrite; uint32_t m_lastPing; @@ -581,7 +589,7 @@ private: bool m_bAutoattack; Common::ZoneingType m_zoningType; - + bool m_bMarkedForZoning; uint32_t m_updateFlags; bool m_bNewAdventurer; @@ -597,6 +605,8 @@ private: uint8_t m_gc; uint8_t m_gcRank[3]; + // content finder info + uint32_t m_cfPenaltyUntil; }; } diff --git a/src/servers/Server_Zone/PlayerSql.cpp b/src/servers/Server_Zone/PlayerSql.cpp index 4ed113a9..7525a2d9 100644 --- a/src/servers/Server_Zone/PlayerSql.cpp +++ b/src/servers/Server_Zone/PlayerSql.cpp @@ -43,7 +43,8 @@ bool Core::Entity::Player::load( uint32_t charId, Core::SessionPtr pSession ) // TODO: can't help but think that the whole player loading could be handled better... const std::string char_id_str = std::to_string( charId ); - auto pQR = g_database.query( "SELECT c.Name, " + auto pQR = g_database.query( "SELECT " + "c.Name, " "c.PrimaryTerritoryId, " "c.Hp, " "c.Mp, " @@ -53,7 +54,7 @@ bool Core::Entity::Player::load( uint32_t charId, Core::SessionPtr pSession ) "c.Pos_0_1, " "c.Pos_0_2, " "c.Pos_0_3, " - "c.FirstLogin, " + "c.FirstLogin, " // 10 "c.Customize, " "c.ModelMainWeapon, " "c.ModelSubWeapon, " @@ -63,7 +64,7 @@ bool Core::Entity::Player::load( uint32_t charId, Core::SessionPtr pSession ) "cd.BirthMonth, " "cd.Status, " "cd.Class, " - "cd.Homepoint, " + "cd.Homepoint, " // 20 "cd.HowTo, " "c.ContentId, " "c.Voice, " @@ -73,11 +74,12 @@ bool Core::Entity::Player::load( uint32_t charId, Core::SessionPtr pSession ) "cd.Aetheryte, " "cd.unlocks, " "cd.Discovery, " - "cd.StartTown, " + "cd.StartTown, " // 30 "cd.TotalPlayTime, " "c.IsNewAdventurer, " "cd.GrandCompany, " - "cd.GrandCompanyRank " + "cd.GrandCompanyRank, " + "cd.CFPenaltyUntil " "FROM charabase AS c " " INNER JOIN charadetail AS cd " " ON c.CharacterId = cd.CharacterId " @@ -166,6 +168,8 @@ bool Core::Entity::Player::load( uint32_t charId, Core::SessionPtr pSession ) m_gc = field[33].getUInt8(); field[34].getBinary( reinterpret_cast< char* >( m_gcRank ), 3 ); + m_cfPenaltyUntil = field[35].getUInt32(); + m_pCell = nullptr; if( !loadActiveQuests() || !loadClassData() || !loadSearchInfo() ) @@ -388,6 +392,11 @@ void Core::Entity::Player::createUpdateSql() charaInfoSearchSet.insert( " SearchComment = UNHEX('" + std::string( Util::binaryToHexString( reinterpret_cast< uint8_t* >( m_searchMessage ), sizeof( m_searchMessage ) ) + "')" ) ); } + if( m_updateFlags & PlayerSyncFlags::CFPenaltyTime ) + { + charaDetailSet.insert( " CFPenaltyUntil = " + std::to_string( m_cfPenaltyUntil ) ); + } + if( !charaInfoSearchSet.empty() ) { for( auto entry : charaInfoSearchSet )