diff --git a/bin/web/assets/img/background.png b/bin/web/assets/img/background.png index a2fae17b..ea97ac81 100644 Binary files a/bin/web/assets/img/background.png and b/bin/web/assets/img/background.png differ diff --git a/scripts/chai/CmnDef/CmnDefCutSceneReplay.chai b/scripts/chai/CmnDef/CmnDefCutSceneReplay.chai new file mode 100644 index 00000000..6f390751 --- /dev/null +++ b/scripts/chai/CmnDef/CmnDefCutSceneReplay.chai @@ -0,0 +1,38 @@ + +class CmnDefCutSceneReplayDef +{ + + def CmnDefCutSceneReplayDef() + { + this.id = 721028; + } + + def Scene00000( player ) + { + player.eventPlay( this.id, 0, 0x2000/*flags*/, 0/*unk*/, 1/*unk*/, + fun( player, eventId, param1, param2, param3 ) + { + if( param2 != 0 ) + { + CmnDefCutSceneReplay.Scene00001( player, param2 ); + } + }); + } + + def Scene00001( player, returnScene ) + { + player.eventPlay( this.id, 1, 0xFB2EC8F8/*flags*/, 0/*unk*/, 1, returnScene, + fun( player, eventId, param1, param2, param3 ) + { + + }); + } + + def onTalk( eventId, player, actorId ) + { + this.Scene00000( player ); + } + +}; + +GLOBAL CmnDefCutSceneReplay = CmnDefCutSceneReplayDef(); \ No newline at end of file diff --git a/scripts/chai/CmnDef/CmnDefInnBed.chai b/scripts/chai/CmnDef/CmnDefInnBed.chai new file mode 100644 index 00000000..4ea1ba9c --- /dev/null +++ b/scripts/chai/CmnDef/CmnDefInnBed.chai @@ -0,0 +1,62 @@ + +class CmnDefInnBedDef +{ + + def CmnDefInnBedDef() + { + this.id = 720916; + } + + def Scene00000( player ) //Menu + { + player.eventPlay( this.id, 0, 0x2000/*flags*/, 0/*unk*/, 1/*unk*/, + fun( player, eventId, param1, param2, param3 ) + { + if( param2 > 1 ) + { + CmnDefInnBed.Scene00001( player, param2 ); + } + + }); + } + + def Scene00001( player, what ) //Lay down + { + player.eventPlay( this.id, 1, 0xF32E48F8/*flags*/, 0/*unk*/, 1/*unk*/, what, + fun( player, eventId, param1, param2, param3 ) + { + CmnDefInnBed.Scene00002( player, param2 ); + }); + } + + def Scene00002( player, what ) //Log out + { + player.eventPlay( this.id, 2, 0xF32E48F8/*flags*/, 0/*unk*/, 1/*unk*/, what, + fun( player, eventId, param1, param2, param3 ) + { + + }); + } + + def Scene00100( player ) //Wake up + { + player.eventPlay( this.id, 100, 0xF32E48F8/*flags*/, 0/*unk*/, 0/*unk*/, + fun( player, eventId, param1, param2, param3 ) + { + + }); + } + + def onTalk( eventId, player, actorId ) + { + this.Scene00000( player ); + } + + def onEnterTerritory( eventId, player, param1, param2 ) + { + this.Scene00100( player ); + } + +}; + +GLOBAL CmnDefInnBed = CmnDefInnBedDef(); \ No newline at end of file diff --git a/scripts/chai/CmnDef/HouFurOrchestrion.chai b/scripts/chai/CmnDef/HouFurOrchestrion.chai new file mode 100644 index 00000000..adc60f55 --- /dev/null +++ b/scripts/chai/CmnDef/HouFurOrchestrion.chai @@ -0,0 +1,26 @@ + +class HouFurOrchestrionDef +{ + + def HouFurOrchestrionDef() + { + this.id = 721226; + } + + def Scene00000( player ) + { + player.eventPlay( this.id, 0, 0x2000/*flags*/, 0/*unk*/, 1/*unk*/, + fun( player, eventId, param1, param2, param3 ) + { + + }); + } + + def onTalk( eventId, player, actorId ) + { + this.Scene00000( player ); + } + +}; + +GLOBAL HouFurOrchestrion = HouFurOrchestrionDef(); \ No newline at end of file diff --git a/sql/charadetail.sql b/sql/charadetail.sql index 05ed0de0..8716365d 100644 --- a/sql/charadetail.sql +++ b/sql/charadetail.sql @@ -70,7 +70,9 @@ CREATE TABLE IF NOT EXISTS `charadetail` ( `StepIndex` int(5) DEFAULT NULL, `ChocoboTaxiStandFlags` binary(8) DEFAULT NULL, `GMRank` int(3) DEFAULT '0', + `EquipDisplayFlags` int(3) DEFAULT '0', `unlocks` binary(64) DEFAULT NULL, + `Orchestrion` binary(38) DEFAULT NULL, `CharacterId` int(20) NOT NULL DEFAULT '0', `IS_DELETE` int(3) DEFAULT '0', `IS_NOT_ACTIVE_FLG` int(3) DEFAULT '0', diff --git a/sql/update.sql b/sql/update.sql index 03b7679d..0f05e32a 100644 --- a/sql/update.sql +++ b/sql/update.sql @@ -22,4 +22,11 @@ -- -- ALTER TABLE `charadetail` CHANGE `OpeningSequence` `OpeningSequence` INT(3) NULL DEFAULT '0'; -- ------------------------------------------- --- update.sql Before Merge into Other SQL's 30/08/2017 \ No newline at end of file +-- update.sql Before Merge into Other SQL's 30/08/2017 +-- ------------------------------------------- +-- ALTER TABLE `charadetail` ADD `EquipDisplayFlags` int(3) DEFAULT '0' AFTER `GMRank`; +-- ------------------------------------------- +-- update.sql before titles added 09/10/2017 + +ALTER TABLE `charadetail` CHANGE `TitleList` `Titlelist` BINARY(48) NULL DEFAULT NULL; +ALTER TABLE `charadetail` ADD COLUMN `Orchestrion` BINARY(38) DEFAULT NULL AFTER `unlocks`; \ No newline at end of file diff --git a/src/servers/Server_Common/Common.h b/src/servers/Server_Common/Common.h index dc4c5fff..eb534594 100644 --- a/src/servers/Server_Common/Common.h +++ b/src/servers/Server_Common/Common.h @@ -288,6 +288,7 @@ namespace Core { Aetherytes = 0x00000080, // Attuned aetherytes HomePoint = 0x00000100, // Current homepoint HowTo = 0x00000200, + Title = 0x00000400, HpMp = 0x00000800, QuestTracker = 0x00001000, @@ -946,9 +947,11 @@ namespace Core { SetMaxGearSets = 0x230, - ToggleDisplayHeadAndWeapon = 0x260, + SetCharaGearParamUI = 0x260, - GearSetEquipMsg = 0x321 + GearSetEquipMsg = 0x321, + + ToggleOrchestrionUnlock = 0x396 }; enum struct ChatType : uint32_t @@ -1056,6 +1059,15 @@ namespace Core { Unused100 }; + enum EquipDisplayFlags : uint8_t + { + HideNothing = 0x0, + HideHead = 0x1, + HideWeapon = 0x2, + + Visor = 0x40, + }; + struct ServerEntry { uint32_t serverId; diff --git a/src/servers/Server_Common/Network/PacketDef/Ipcs.h b/src/servers/Server_Common/Network/PacketDef/Ipcs.h index cff0ef1b..3dcba357 100644 --- a/src/servers/Server_Common/Network/PacketDef/Ipcs.h +++ b/src/servers/Server_Common/Network/PacketDef/Ipcs.h @@ -109,15 +109,19 @@ namespace Packets { QuestCompleteList = 0x017F, // updated 4.1 QuestFinish = 0x0180, // updated 4.1 QuestMessage = 0x0179, - QuestTracker = 0x0181, - ActorSpawn = 0x0190, - ActorFreeSpawn = 0x0191, - InitZone = 0x019A, - WeatherChange = 0x01AF, - Discovery = 0x01B2, + + QuestTracker = 0x0181, // updated for sb + ActorSpawn = 0x0190, // todo: split into playerspawn/actorspawn and use opcode 0x110/0x111 + ActorFreeSpawn = 0x0191, // unchanged for sb + InitZone = 0x019A, // unchanged for sb + WeatherChange = 0x01AF, // updated for sb + PlayerTitleList = 0x01B1, // updated for 4.06 + Discovery = 0x01B2, // updated for sb EorzeaTimeOffset = 0x01B4, + EquipDisplayFlags = 0x01C0, + CFAvailableContents = 0x01CF, PrepareZoning = 0x0248, // updated 4.1 @@ -183,8 +187,12 @@ namespace Packets { ReturnEventHandler = 0x0131, // updated 4.1 TradeReturnEventHandler = 0x0132, // updated 4.1 + LinkshellEventHandler = 0x0144, // updated 4.1 ?? LinkshellEventHandler1 = 0x0145, // updated 4.1 ?? + + ReqEquipDisplayFlagsChange = 0x014C, // updated 4.1 ?? + }; //////////////////////////////////////////////////////////////////////////////// diff --git a/src/servers/Server_Common/Network/PacketDef/Zone/ServerZoneDef.h b/src/servers/Server_Common/Network/PacketDef/Zone/ServerZoneDef.h index 10a34cd0..327a0847 100644 --- a/src/servers/Server_Common/Network/PacketDef/Zone/ServerZoneDef.h +++ b/src/servers/Server_Common/Network/PacketDef/Zone/ServerZoneDef.h @@ -368,8 +368,8 @@ struct FFXIVIpcPlayerSpawn : FFXIVIpcBasePacket uint32_t u20; uint32_t ownerId; uint32_t u22; - uint32_t hPCurr; uint32_t hPMax; + uint32_t hPCurr; uint32_t displayFlags; uint16_t fateID; uint16_t mPCurr; @@ -639,6 +639,14 @@ struct FFXIVIpcUpdateClassInfo : FFXIVIpcBasePacket uint32_t restedExp; }; +/** + * Structural representation of the packet sent by the server + * to send the titles available to the player + */ +struct FFXIVIpcPlayerTitleList : FFXIVIpcBasePacket +{ + uint8_t titleList[48]; +}; /** * Structural representation of the packet sent by the server @@ -781,7 +789,7 @@ struct FFXIVIpcInitUI : FFXIVIpcBasePacket uint8_t unknownRest[32]; uint8_t tripleTriadCards[26]; uint8_t unknownRest1[21]; - uint8_t orchestrionMask[19]; + uint8_t orchestrionMask[38]; uint8_t hallOfNoviceCompleteMask[3]; uint8_t unknownMask2[11]; uint8_t unknownMask3[16]; @@ -1286,6 +1294,14 @@ struct FFXIVIpcEorzeaTimeOffset : FFXIVIpcBasePacket uint64_t timestamp; }; +/** +* Structural representation of the packet sent by the server +* to set the gear show/hide status of a character +*/ +struct FFXIVIpcEquipDisplayFlags : FFXIVIpcBasePacket +{ + uint8_t bitmask; +}; } /* Server */ diff --git a/src/servers/Server_REST/PlayerMinimal.cpp b/src/servers/Server_REST/PlayerMinimal.cpp index 9643a35b..f36a14a1 100644 --- a/src/servers/Server_REST/PlayerMinimal.cpp +++ b/src/servers/Server_REST/PlayerMinimal.cpp @@ -175,6 +175,12 @@ namespace Core { char unlocks[64]; memset( unlocks, 0, 64 ); + char titleList[48]; + memset( titleList, 0, 48 ); + + char orchestrion[38]; + memset( orchestrion, 0, 38 ); + int16_t questTracking[5] = { -1, -1, -1, -1, -1 }; uint16_t size = static_cast< uint16_t >( m_lookMap.size() ); @@ -270,6 +276,8 @@ namespace Core { " unlocks, " " QuestTracking, " " Aetheryte, " + " TitleList, " + " Orchestrion, " " GMRank, " " UPDATE_DATE ) " " VALUES (" + std::to_string( m_iD ) + ", " @@ -285,6 +293,8 @@ namespace Core { + "UNHEX('" + std::string( Util::binaryToHexString( (uint8_t*)unlocks, 64 ) ) + "'), " + "UNHEX('" + std::string( Util::binaryToHexString( (uint8_t*)questTracking, 10 ) ) + "'), " + "UNHEX('" + std::string( Util::binaryToHexString( (uint8_t*)aetherytes, 12 ) ) + "')," + + "UNHEX('" + std::string( Util::binaryToHexString( (uint8_t*)titleList, 48 ) ) + "')," + + "UNHEX('" + std::string( Util::binaryToHexString( (uint8_t*)orchestrion, 38 ) ) + "')," + std::to_string( m_gmRank ) + ", NOW());" ); diff --git a/src/servers/Server_Zone/Action/ActionCast.cpp b/src/servers/Server_Zone/Action/ActionCast.cpp index df132950..a28125c7 100644 --- a/src/servers/Server_Zone/Action/ActionCast.cpp +++ b/src/servers/Server_Zone/Action/ActionCast.cpp @@ -92,7 +92,11 @@ void Core::Action::ActionCast::onInterrupt() m_pSource->getAsPlayer()->sendStateFlags(); auto control = ActorControlPacket142( m_pSource->getId(), ActorControlType::CastInterrupt, - 0x219, 1, m_id, 1 ); + 0x219, 1, m_id, 0 ); + + // Note: When cast interrupt from taking too much damage, set the last value to 1. This enables the cast interrupt effect. Example: + // auto control = ActorControlPacket142( m_pSource->getId(), ActorControlType::CastInterrupt, 0x219, 1, m_id, 0 ); + m_pSource->sendToInRangeSet( control, true ); } diff --git a/src/servers/Server_Zone/Actor/Actor.cpp b/src/servers/Server_Zone/Actor/Actor.cpp index 977cdd6e..33f20c6b 100644 --- a/src/servers/Server_Zone/Actor/Actor.cpp +++ b/src/servers/Server_Zone/Actor/Actor.cpp @@ -696,9 +696,13 @@ void Core::Entity::Actor::handleScriptSkill( uint32_t type, uint32_t actionId, u if ( isPlayer() && !ActionCollision::isActorApplicable( pTarget.shared_from_this(), TargetFilter::Enemies ) ) break; - pTarget.takeDamage( static_cast< uint32_t >( param1 ) ); - pTarget.onActionHostile( shared_from_this() ); sendToInRangeSet( effectPacket, true ); + + pTarget.takeDamage( static_cast< uint32_t >( param1 ) ); + + if ( pTarget.isAlive() ) + pTarget.onActionHostile( shared_from_this() ); + } else { @@ -711,8 +715,11 @@ void Core::Entity::Actor::handleScriptSkill( uint32_t type, uint32_t actionId, u effectPacket.data().effectTarget = pHitActor->getId(); sendToInRangeSet( effectPacket, true ); // todo: send to range of what? ourselves? when mob script hits this is going to be lacking + pHitActor->takeDamage( static_cast< uint32_t >( param1 ) ); - pHitActor->onActionHostile( shared_from_this() ); + + if( pHitActor->isAlive() ) + pHitActor->onActionHostile( shared_from_this() ); // Debug if ( isPlayer() ) diff --git a/src/servers/Server_Zone/Actor/Actor.h b/src/servers/Server_Zone/Actor/Actor.h index 215ec666..5a784f28 100644 --- a/src/servers/Server_Zone/Actor/Actor.h +++ b/src/servers/Server_Zone/Actor/Actor.h @@ -43,6 +43,16 @@ public: Active = 1, }; + enum DisplayFlags : uint16_t + { + ActiveStance = 0x001, + Invisible = 0x020, + HideHead = 0x040, + HideWeapon = 0x080, + Faded = 0x100, + Visor = 0x800, + }; + enum struct ActorStatus : uint8_t { Idle = 0x01, diff --git a/src/servers/Server_Zone/Actor/Player.cpp b/src/servers/Server_Zone/Actor/Player.cpp index 54b7270e..0199f3ca 100644 --- a/src/servers/Server_Zone/Actor/Player.cpp +++ b/src/servers/Server_Zone/Actor/Player.cpp @@ -388,6 +388,11 @@ void Core::Entity::Player::setZone( uint32_t zoneId ) sendInventory(); + if( isLogin() ) + { + queuePacket(ActorControlPacket143( getId(), SetCharaGearParamUI, m_equipDisplayFlags, 1 ) ); + } + // set flags, will be reset automatically by zoning ( only on client side though ) pPlayer->setStateFlag( PlayerStateFlag::BetweenAreas ); pPlayer->setStateFlag( PlayerStateFlag::BetweenAreas1 ); @@ -603,6 +608,18 @@ void Core::Entity::Player::learnAction( uint8_t actionId ) queuePacket( ActorControlPacket143( getId(), ToggleActionUnlock, actionId, 1 ) ); } +void Core::Entity::Player::learnSong( uint8_t songId, uint32_t itemId ) +{ + uint16_t index; + uint8_t value; + Util::valueToFlagByteIndexValue( songId, value, index ); + + m_orchestrion[index] |= value; + + setSyncFlag( Unlocks ); + queuePacket( ActorControlPacket143( getId(), ToggleOrchestrionUnlock, songId, 1, itemId ) ); +} + bool Core::Entity::Player::isActionLearned( uint8_t actionId ) const { uint16_t index; @@ -1192,6 +1209,11 @@ const uint8_t * Core::Entity::Player::getUnlockBitmask() const return m_unlocks; } +const uint8_t * Core::Entity::Player::getOrchestrionBitmask() const +{ + return m_orchestrion; +} + uint64_t Core::Entity::Player::getContentId() const { return m_contentId; @@ -1415,6 +1437,55 @@ void Core::Entity::Player::setIsLogin( bool bIsLogin ) m_bIsLogin = bIsLogin; } +uint8_t * Core::Entity::Player::getTitleList() +{ + return m_titleList; +} + +uint16_t Core::Entity::Player::getTitle() const +{ + return m_title; +} + +void Core::Entity::Player::addTitle( uint16_t titleId ) +{ + uint16_t index; + uint8_t value; + Util::valueToFlagByteIndexValue( titleId, value, index ); + + m_titleList[index] |= value; + setSyncFlag( PlayerSyncFlags::Title ); +} + +void Core::Entity::Player::setTitle( uint16_t titleId ) +{ + uint16_t index; + uint8_t value; + Util::valueToFlagByteIndexValue( titleId, value, index ); + + if ( ( m_titleList[index] & value ) == 0 ) // Player doesn't have title - bail + return; + + m_title = titleId; + + sendToInRangeSet( ActorControlPacket142( getId(), SetTitle, titleId ), true ); + setSyncFlag( PlayerSyncFlags::Title ); +} + +void Core::Entity::Player::setEquipDisplayFlags( uint8_t state ) +{ + m_equipDisplayFlags = state; + GamePacketNew< FFXIVIpcEquipDisplayFlags, ServerZoneIpcType > paramPacket( getId() ); + paramPacket.data().bitmask = m_equipDisplayFlags; + sendToInRangeSet( paramPacket, true ); + setSyncFlag( PlayerSyncFlags::Status ); +} + +uint8_t Core::Entity::Player::getEquipDisplayFlags() const +{ + return m_equipDisplayFlags; +} + void Core::Entity::Player::autoAttack( ActorPtr pTarget ) { diff --git a/src/servers/Server_Zone/Actor/Player.h b/src/servers/Server_Zone/Actor/Player.h index 1001251a..488065df 100644 --- a/src/servers/Server_Zone/Actor/Player.h +++ b/src/servers/Server_Zone/Actor/Player.h @@ -328,6 +328,18 @@ public: void teleport( uint16_t aetheryteId, uint8_t type = 1 ); /*! prepares zoning / fades out the screen */ void prepareZoning( uint16_t targetZone, bool fadeOut, uint8_t fadoutTime = 0, uint16_t animation = 0 ); + /*! get player's title list (available titles) */ + uint8_t * getTitleList(); + /*! get player's active title */ + uint16_t getTitle() const; + /*! add title to player title list */ + void addTitle( uint16_t titleId ); + /*! change player's active title */ + void setTitle( uint16_t titleId ); + /*! change gear param state */ + void setEquipDisplayFlags( uint8_t state ); + /*! get gear param state and send update to inRangeSet */ + uint8_t getEquipDisplayFlags() const; void calculateStats() override; void sendStats(); @@ -361,10 +373,14 @@ public: void updateHowtosSeen( uint32_t howToId ); /*! learn an action / update the unlock bitmask. */ void learnAction( uint8_t actionId ); + /*! learn a song / update the unlock bitmask. */ + void learnSong( uint8_t songId, uint32_t itemId ); /*! check if an action is already unlocked in the bitmask. */ bool isActionLearned( uint8_t actionId ) const; /*! return a const pointer to the unlock bitmask array */ const uint8_t * getUnlockBitmask() const; + /*! return a const pointer to the orchestrion bitmask array */ + const uint8_t * getOrchestrionBitmask() const; // Spawn handling @@ -565,7 +581,8 @@ private: uint8_t status; } m_retainerInfo[8]; - uint8_t m_titleList[32]; + uint16_t m_title; + uint8_t m_titleList[48]; uint8_t m_achievement[16]; uint8_t m_howTo[33]; uint8_t m_homePoint; @@ -581,6 +598,7 @@ private: uint32_t m_expArray[25]; uint8_t m_aetheryte[16]; uint8_t m_unlocks[64]; + uint8_t m_orchestrion[38]; uint8_t m_openingSequence; @@ -599,6 +617,8 @@ private: uint8_t m_gmRank; uint16_t zoneId; + uint8_t m_equipDisplayFlags; + bool m_bInCombat; bool m_bLoadingComplete; bool m_bAutoattack; diff --git a/src/servers/Server_Zone/Actor/PlayerSql.cpp b/src/servers/Server_Zone/Actor/PlayerSql.cpp index 7d5db611..4ff24919 100644 --- a/src/servers/Server_Zone/Actor/PlayerSql.cpp +++ b/src/servers/Server_Zone/Actor/PlayerSql.cpp @@ -81,7 +81,11 @@ bool Core::Entity::Player::load( uint32_t charId, Core::SessionPtr pSession ) "cd.GrandCompanyRank, " "cd.CFPenaltyUntil, " "cd.OpeningSequence, " - "cd.GMRank " + "cd.GMRank, " + "cd.EquipDisplayFlags, " + "cd.ActiveTitle, " + "cd.TitleList, " // 40 + "cd.Orchestrion " "FROM charabase AS c " " INNER JOIN charadetail AS cd " " ON c.CharacterId = cd.CharacterId " @@ -89,7 +93,7 @@ bool Core::Entity::Player::load( uint32_t charId, Core::SessionPtr pSession ) if( !pQR ) { - g_log.error( "Player id " + char_id_str + " does not exist!" ); + g_log.error( "[DB] Failed loading Player ID " + char_id_str ); return false; } @@ -173,6 +177,12 @@ bool Core::Entity::Player::load( uint32_t charId, Core::SessionPtr pSession ) m_openingSequence = field[36].get< uint32_t >(); m_gmRank = field[37].get< uint8_t >(); + m_equipDisplayFlags = field[38].get< uint8_t >(); + + m_title = field[39].get< uint8_t >(); + field[40].getBinary( reinterpret_cast< char* >( m_titleList ), sizeof( m_titleList ) ); + + field[41].getBinary( reinterpret_cast< char* >( m_orchestrion ), sizeof( m_orchestrion ) ); m_pCell = nullptr; @@ -300,22 +310,32 @@ void Core::Entity::Player::createUpdateSql() charaDetailSet.insert( " Homepoint = " + std::to_string( m_homePoint ) ); if( m_updateFlags & PlayerSyncFlags::Discovery ) - charaDetailSet.insert( " Discovery = UNHEX('" + std::string( Util::binaryToHexString( static_cast< uint8_t* >( m_discovery ), sizeof( m_discovery ) ) ) + "')" ); + charaDetailSet.insert( " Discovery = UNHEX('" + Util::binaryToHexString( static_cast< uint8_t* >( m_discovery ), sizeof( m_discovery ) ) + "')" ); if( m_updateFlags & PlayerSyncFlags::PlayTime ) charaDetailSet.insert( " TotalPlayTime = " + std::to_string( m_playTime ) ); if( m_updateFlags & PlayerSyncFlags::Unlocks ) - charaDetailSet.insert( " unlocks = UNHEX('" + std::string( Util::binaryToHexString( static_cast< uint8_t* >( m_unlocks ), sizeof( m_unlocks ) ) ) + "')" ); + { + charaDetailSet.insert( " unlocks = UNHEX('" + Util::binaryToHexString( static_cast< uint8_t* >( m_unlocks ), sizeof( m_unlocks ) ) + "')"); + charaDetailSet.insert( " Orchestrion = UNHEX('" + Util::binaryToHexString( static_cast< uint8_t* >( m_orchestrion ), sizeof( m_orchestrion ) ) + "')" ); + } + if( m_updateFlags & PlayerSyncFlags::QuestTracker ) - charaDetailSet.insert( " QuestTracking = UNHEX('" + std::string( Util::binaryToHexString( reinterpret_cast< uint8_t* >( m_questTracking ), sizeof( m_questTracking ) ) ) + "')" ); + charaDetailSet.insert( " QuestTracking = UNHEX('" + Util::binaryToHexString( reinterpret_cast< uint8_t* >( m_questTracking ), sizeof( m_questTracking ) ) + "')" ); if( m_updateFlags & PlayerSyncFlags::HowTo ) - charaDetailSet.insert( " HowTo = UNHEX('" + std::string( Util::binaryToHexString( static_cast< uint8_t* >( m_howTo ), sizeof( m_howTo ) ) ) + "')" ); + charaDetailSet.insert( " HowTo = UNHEX('" + Util::binaryToHexString( static_cast< uint8_t* >( m_howTo ), sizeof( m_howTo ) ) + "')" ); + + if ( m_updateFlags & PlayerSyncFlags::Title ) + { + charaDetailSet.insert( " ActiveTitle = " + std::to_string( m_title ) ); + charaDetailSet.insert( " TitleList = UNHEX('" + Util::binaryToHexString( reinterpret_cast< uint8_t* >( m_titleList ), sizeof( m_titleList ) ) + "')" ); + } if( m_updateFlags & PlayerSyncFlags::Aetherytes ) - charaDetailSet.insert( " Aetheryte = UNHEX('" + std::string( Util::binaryToHexString( reinterpret_cast< uint8_t* >( m_aetheryte ), sizeof( m_aetheryte ) ) ) + "')" ); + charaDetailSet.insert( " Aetheryte = UNHEX('" + Util::binaryToHexString( reinterpret_cast< uint8_t* >( m_aetheryte ), sizeof( m_aetheryte ) ) + "')" ); if( m_updateFlags & PlayerSyncFlags::NewGame ) @@ -327,7 +347,7 @@ void Core::Entity::Player::createUpdateSql() if( m_updateFlags & PlayerSyncFlags::GC ) { charaDetailSet.insert( " GrandCompany = " + std::to_string( m_gc ) ); - charaDetailSet.insert( " GrandCompanyRank = UNHEX('" + std::string( Util::binaryToHexString( reinterpret_cast< uint8_t* >( m_gcRank ), sizeof( m_gcRank ) ) ) + "')" ); + charaDetailSet.insert( " GrandCompanyRank = UNHEX('" + Util::binaryToHexString( reinterpret_cast< uint8_t* >( m_gcRank ), sizeof( m_gcRank ) ) + "')" ); } if( m_updateFlags & PlayerSyncFlags::CFPenaltyTime ) @@ -347,9 +367,10 @@ void Core::Entity::Player::createUpdateSql() charaBaseSet.insert( " Hp = " + std::to_string( getHp() ) ); charaBaseSet.insert( " Mp = " + std::to_string( getMp() ) ); charaBaseSet.insert( " Mode = " + std::to_string( static_cast< uint32_t >( getStance() ) ) ); - charaBaseSet.insert( " ModelEquip = UNHEX('" + std::string( Util::binaryToHexString( reinterpret_cast< uint8_t* >( m_modelEquip ), 40 ) ) + "')" ); + charaBaseSet.insert( " ModelEquip = UNHEX('" + Util::binaryToHexString( reinterpret_cast< uint8_t* >( m_modelEquip ), 40 ) + "')" ); charaDetailSet.insert( " Class = " + std::to_string( static_cast< uint32_t >( getClass() ) ) ); charaDetailSet.insert( " Status = " + std::to_string( static_cast< uint8_t >( getStatus() ) ) ); + charaDetailSet.insert( " EquipDisplayFlags = " + std::to_string( static_cast< uint8_t >( getEquipDisplayFlags() ) ) ); } if( m_updateFlags & PlayerSyncFlags::OpeningSeq ) @@ -359,7 +380,7 @@ void Core::Entity::Player::createUpdateSql() if( m_updateFlags & PlayerSyncFlags::Quests ) { - charaDetailSet.insert( " QuestCompleteFlags = UNHEX('" + std::string( Util::binaryToHexString( static_cast< uint8_t* >( m_questCompleteFlags ), 200 ) ) + "')" ); + charaDetailSet.insert( " QuestCompleteFlags = UNHEX('" + Util::binaryToHexString( static_cast< uint8_t* >( m_questCompleteFlags ), 200 ) + "')" ); for( int32_t i = 0; i < 30; i++ ) { @@ -396,7 +417,7 @@ void Core::Entity::Player::createUpdateSql() { charaInfoSearchSet.insert( " SelectClassId = " + std::to_string( m_searchSelectClass ) ); charaInfoSearchSet.insert( " SelectRegion = " + std::to_string( m_searchSelectRegion ) ); - charaInfoSearchSet.insert( " SearchComment = UNHEX('" + std::string( Util::binaryToHexString( reinterpret_cast< uint8_t* >( m_searchMessage ), sizeof( m_searchMessage ) ) + "')" ) ); + charaInfoSearchSet.insert( " SearchComment = UNHEX('" + Util::binaryToHexString( reinterpret_cast< uint8_t* >( m_searchMessage ), sizeof( m_searchMessage ) ) + "')" ); } if( !charaInfoSearchSet.empty() ) diff --git a/src/servers/Server_Zone/DebugCommand/DebugCommandHandler.cpp b/src/servers/Server_Zone/DebugCommand/DebugCommandHandler.cpp index 70c413c0..3a8805ed 100644 --- a/src/servers/Server_Zone/DebugCommand/DebugCommandHandler.cpp +++ b/src/servers/Server_Zone/DebugCommand/DebugCommandHandler.cpp @@ -118,6 +118,7 @@ void Core::DebugCommandHandler::scriptReload( char * data, Core::Entity::PlayerP boost::shared_ptr command ) { g_scriptMgr.reload(); + pPlayer->sendDebug( "Scripts reloaded." ); } void Core::DebugCommandHandler::set( char * data, Core::Entity::PlayerPtr pPlayer, boost::shared_ptr command ) @@ -182,13 +183,6 @@ void Core::DebugCommandHandler::set( char * data, Core::Entity::PlayerPtr pPlaye pPlayer->teleport( aetheryteId ); } - - else if( ( subCommand == "unlockaetheryte" ) && ( params != "" ) ) - { - for( uint8_t i = 0; i < 255; i++ ) - pPlayer->registerAetheryte( i ); - } - else if( ( subCommand == "discovery" ) && ( params != "" ) ) { int32_t map_id; @@ -304,13 +298,21 @@ void Core::DebugCommandHandler::add( char * data, Core::Entity::PlayerPtr pPlaye int32_t duration; uint16_t param; - sscanf( params.c_str(), "%d %d %hd", &id, &duration, ¶m ); + sscanf( params.c_str(), "%d %d %hu", &id, &duration, ¶m ); StatusEffect::StatusEffectPtr effect( new StatusEffect::StatusEffect( id, pPlayer, pPlayer, duration, 3000 ) ); effect->setParam( param ); pPlayer->addStatusEffect( effect ); } + else if ( subCommand == "title" ) + { + uint32_t titleId; + sscanf( params.c_str(), "%u", &titleId ); + + pPlayer->addTitle( titleId ); + pPlayer->sendNotice( "Added title (ID: " + std::to_string( titleId ) + ")" ); + } else if( subCommand == "spawn" ) { int32_t model, name; @@ -333,7 +335,6 @@ void Core::DebugCommandHandler::add( char * data, Core::Entity::PlayerPtr pPlaye pPlayer->queuePacket( pPe ); } else if( subCommand == "actrl" ) - { // temporary research packet diff --git a/src/servers/Server_Zone/Network/GameConnection.cpp b/src/servers/Server_Zone/Network/GameConnection.cpp index 0563b007..a89d1bfe 100644 --- a/src/servers/Server_Zone/Network/GameConnection.cpp +++ b/src/servers/Server_Zone/Network/GameConnection.cpp @@ -93,8 +93,9 @@ Core::Network::GameConnection::GameConnection( Core::Network::HivePtr pHive, setZoneHandler( ClientZoneIpcType::CFRegisterRoulette, "CFRegisterRoulette", &GameConnection::cfRegisterRoulette ); setZoneHandler( ClientZoneIpcType::CFCommenceHandler, "CFDutyAccepted", &GameConnection::cfDutyAccepted); + setZoneHandler( ClientZoneIpcType::ReqEquipDisplayFlagsChange, "ReqEquipDisplayFlagsChange",&GameConnection::reqEquipDisplayFlagsHandler); - setChatHandler( ClientChatIpcType::TellReq, "TellReq", &GameConnection::tellHandler); + setChatHandler( ClientChatIpcType::TellReq, "TellReq", &GameConnection::tellHandler); } @@ -116,7 +117,7 @@ void Core::Network::GameConnection::OnAccept( const std::string & host, uint16_t void Core::Network::GameConnection::OnDisconnect() { - g_log.debug( "DISCONNECT" ); + g_log.debug( "GameConnection DISCONNECT" ); m_pSession = nullptr; } @@ -167,7 +168,7 @@ void Core::Network::GameConnection::OnRecv( std::vector< uint8_t > & buffer ) void Core::Network::GameConnection::OnError( const boost::system::error_code & error ) { - g_log.debug( "ERROR" ); + g_log.debug( "GameConnection ERROR: " + error.message() ); } void Core::Network::GameConnection::queueInPacket( Core::Network::Packets::GamePacketPtr inPacket ) @@ -380,10 +381,21 @@ void Core::Network::GameConnection::handlePackets( const Core::Network::Packets: { g_log.info( "[" + std::string( id ) + "] Session not registered, creating" ); // return; - g_serverZone.createSession( playerId ); + if( !g_serverZone.createSession( playerId ) ) + { + Disconnect(); + return; + } session = g_serverZone.getSession( playerId ); } + if( !session->isValid() ) //TODO: Catch more things in lobby and send real errors + { + g_log.error( "[" + std::string(id) + "] Session INVALID, disconnecting" ); + Disconnect(); + return; + } + // if not set, set the session for this connection if( !m_pSession && session ) m_pSession = session; @@ -418,8 +430,6 @@ void Core::Network::GameConnection::handlePackets( const Core::Network::Packets: sendSinglePacket( &pPe ); } - - break; } diff --git a/src/servers/Server_Zone/Network/GameConnection.h b/src/servers/Server_Zone/Network/GameConnection.h index d9156583..d89ae491 100644 --- a/src/servers/Server_Zone/Network/GameConnection.h +++ b/src/servers/Server_Zone/Network/GameConnection.h @@ -116,10 +116,10 @@ public: DECLARE_HANDLER( gm1Handler ); DECLARE_HANDLER( gm2Handler ); + DECLARE_HANDLER( reqEquipDisplayFlagsHandler ); + DECLARE_HANDLER( tellHandler ); - - }; diff --git a/src/servers/Server_Zone/Network/Handlers/ActionHandler.cpp b/src/servers/Server_Zone/Network/Handlers/ActionHandler.cpp index 01f2e024..9012ef65 100644 --- a/src/servers/Server_Zone/Network/Handlers/ActionHandler.cpp +++ b/src/servers/Server_Zone/Network/Handlers/ActionHandler.cpp @@ -114,10 +114,23 @@ void Core::Network::GameConnection::actionHandler( const Packets::GamePacket& in } case 0x69: // Cancel cast { - if( pPlayer->checkAction() ) + if( pPlayer->getCurrentAction() != nullptr ) pPlayer->getCurrentAction()->setInterrupted(); break; } + case 0x12E: // Set player title + { + pPlayer->setTitle( param1 ); + break; + } + case 0x12F: // Get title list + { + GamePacketNew< FFXIVIpcPlayerTitleList, ServerZoneIpcType > titleListPacket( pPlayer->getId() ); + memcpy( titleListPacket.data().titleList, pPlayer->getTitleList(), sizeof( titleListPacket.data().titleList ) ); + + pPlayer->queuePacket( titleListPacket ); + break; + } case 0x133: // Update howtos seen { uint32_t howToId = static_cast< uint32_t >( param1 ); diff --git a/src/servers/Server_Zone/Network/Handlers/GMCommandHandlers.cpp b/src/servers/Server_Zone/Network/Handlers/GMCommandHandlers.cpp index 36c1949a..cae64bc6 100644 --- a/src/servers/Server_Zone/Network/Handlers/GMCommandHandlers.cpp +++ b/src/servers/Server_Zone/Network/Handlers/GMCommandHandlers.cpp @@ -72,6 +72,8 @@ enum GmCommand Exp = 0x0068, Inv = 0x006A, + Orchestrion = 0x0074, + Item = 0x00C8, Gil = 0x00C9, Collect = 0x00CA, @@ -84,6 +86,7 @@ enum GmCommand QuestInspect = 0x0131, GC = 0x0154, GCRank = 0x0155, + Aetheryte = 0x015E, Teri = 0x0258, TeriInfo = 0x025D, Jump = 0x025E, @@ -364,6 +367,52 @@ void Core::Network::GameConnection::gm1Handler( const Packets::GamePacket& inPac " was switched." ); break; } + case GmCommand::Aetheryte: + { + if( param1 == 0 ) + { + if( param2 == 0 ) + { + for( uint8_t i = 0; i < 255; i++ ) + targetActor->getAsPlayer()->registerAetheryte( i ); + + pPlayer->sendNotice( "All Aetherytes for " + targetPlayer->getName() + + " were turned on." ); + } + else + { + targetActor->getAsPlayer()->registerAetheryte( param2 ); + pPlayer->sendNotice( "Aetheryte " + std::to_string( param2 ) + " for " + targetPlayer->getName() + + " was turned on." ); + } + } + + + break; + } + case GmCommand::Orchestrion: + { + if( param1 == 1 ) + { + if( param2 == 0 ) + { + for( uint8_t i = 0; i < 255; i++ ) + targetActor->getAsPlayer()->learnSong( i, 0 ); + + pPlayer->sendNotice( "All Songs for " + targetPlayer->getName() + + " were turned on." ); + } + else + { + targetActor->getAsPlayer()->learnSong( param2, 0 ); + pPlayer->sendNotice( "Song " + std::to_string( param2 ) + " for " + targetPlayer->getName() + + " was turned on." ); + } + } + + + break; + } default: pPlayer->sendUrgent( "GM1 Command not implemented: " + std::to_string( commandId ) ); diff --git a/src/servers/Server_Zone/Network/Handlers/PacketHandlers.cpp b/src/servers/Server_Zone/Network/Handlers/PacketHandlers.cpp index 8a438e39..fc8add9f 100644 --- a/src/servers/Server_Zone/Network/Handlers/PacketHandlers.cpp +++ b/src/servers/Server_Zone/Network/Handlers/PacketHandlers.cpp @@ -285,7 +285,11 @@ void Core::Network::GameConnection::updatePositionHandler( const Packets::GamePa } - +void Core::Network::GameConnection::reqEquipDisplayFlagsHandler( const Packets::GamePacket& inPacket, + Entity::PlayerPtr pPlayer ) +{ + pPlayer->setEquipDisplayFlags( inPacket.getValAt< uint8_t >( 0x20 ) ); +} void Core::Network::GameConnection::zoneLineHandler( const Packets::GamePacket& inPacket, Entity::PlayerPtr pPlayer ) diff --git a/src/servers/Server_Zone/Network/PacketWrappers/InitUIPacket.h b/src/servers/Server_Zone/Network/PacketWrappers/InitUIPacket.h index 1a4d06e5..60b69f1d 100644 --- a/src/servers/Server_Zone/Network/PacketWrappers/InitUIPacket.h +++ b/src/servers/Server_Zone/Network/PacketWrappers/InitUIPacket.h @@ -42,7 +42,7 @@ private: m_data.namedayMonth = player->getBirthMonth(); m_data.namedayDay = player->getBirthDay(); // TODO: Support grand company status. - m_data.grandCompany = static_cast< Common::GrandCompany >( player->getStartTown() ); + m_data.grandCompany = static_cast< Common::GrandCompany >( player->getGc() ); //m_data.gcRank = GCRank::None; // TODO: Support starting city. @@ -62,6 +62,8 @@ private: m_data.exp[i] = player->getExpArray()[i]; } + memcpy( m_data.orchestrionMask, player->getOrchestrionBitmask(), sizeof( m_data.orchestrionMask ) ); + memcpy( m_data.unlockBitmask, player->getUnlockBitmask(), sizeof( m_data.unlockBitmask ) ); memcpy( m_data.discovery, player->getDiscoveryBitmask(), sizeof( m_data.discovery ) ); diff --git a/src/servers/Server_Zone/Network/PacketWrappers/PlayerSpawnPacket.h b/src/servers/Server_Zone/Network/PacketWrappers/PlayerSpawnPacket.h index 5bd39a09..a2ded446 100644 --- a/src/servers/Server_Zone/Network/PacketWrappers/PlayerSpawnPacket.h +++ b/src/servers/Server_Zone/Network/PacketWrappers/PlayerSpawnPacket.h @@ -36,41 +36,47 @@ namespace Server { // TODO: temporary gm rank //m_data.gmRank = 0xff; - - m_data.currentMount = 0; m_data.classJob = pPlayer->getClass(); //m_data.status = static_cast< uint8_t >( pPlayer->getStatus() ); + m_data.hPCurr = pPlayer->getHp(); m_data.mPCurr = pPlayer->getMp(); m_data.tPCurr = pPlayer->getTp(); m_data.hPMax = pPlayer->getMaxHp(); m_data.mPMax = pPlayer->getMaxMp(); - m_data.gmRank = pPlayer->getGmRank(); + //m_data.tPMax = 3000; m_data.level = pPlayer->getLevel(); + m_data.gmRank = pPlayer->getGmRank(); memcpy( m_data.look, pPlayer->getLookArray(), 26 ); + auto item = pPlayer->getInventory()->getItemAt( Inventory::GearSet0, Inventory::EquipSlot::MainHand ); if( item ) m_data.mainWeaponModel = item->getModelId1(); m_data.secWeaponModel = pPlayer->getModelSubWeapon(); + m_data.models[0] = pPlayer->getModelForSlot( Inventory::EquipSlot::Head ); m_data.models[1] = pPlayer->getModelForSlot( Inventory::EquipSlot::Body ); m_data.models[2] = pPlayer->getModelForSlot( Inventory::EquipSlot::Hands ); m_data.models[3] = pPlayer->getModelForSlot( Inventory::EquipSlot::Legs ); m_data.models[4] = pPlayer->getModelForSlot( Inventory::EquipSlot::Feet ); strcpy( m_data.name, pPlayer->getName().c_str() ); + m_data.pos.x = pPlayer->getPos().x; m_data.pos.y = pPlayer->getPos().y; m_data.pos.z = pPlayer->getPos().z; - m_data.voice = pPlayer->getVoiceId(); - m_data.rotation = Math::Util::floatToUInt16Rot( pPlayer->getRotation() ); + + + m_data.title = pPlayer->getTitle(); + m_data.voice = pPlayer->getVoiceId(); + m_data.currentMount = 0; m_data.onlineStatus = static_cast< uint8_t >( pPlayer->getOnlineStatus() ); //m_data.u23 = 0x04; //m_data.u24 = 256; - m_data.state = 1; + m_data.state = static_cast< uint8_t >( pPlayer->getStatus() ); m_data.type = 1; if( pTarget == pPlayer ) { @@ -85,7 +91,22 @@ namespace Server { if( pPlayer->getZoningType() != Common::ZoneingType::None ) { - m_data.displayFlags |= 0x20; + m_data.displayFlags |= Entity::Actor::DisplayFlags::Invisible; + } + + if( pPlayer->getEquipDisplayFlags() & Core::Common::EquipDisplayFlags::HideHead ) + { + m_data.displayFlags |= Entity::Actor::DisplayFlags::HideHead; + } + + if( pPlayer->getEquipDisplayFlags() & Core::Common::EquipDisplayFlags::HideWeapon ) + { + m_data.displayFlags |= Entity::Actor::DisplayFlags::HideWeapon; + } + + if( pPlayer->getEquipDisplayFlags() & Core::Common::EquipDisplayFlags::Visor ) + { + m_data.displayFlags |= Entity::Actor::DisplayFlags::Visor; } m_data.targetId = pPlayer->getTargetId(); diff --git a/src/servers/Server_Zone/Session.cpp b/src/servers/Server_Zone/Session.cpp index 5754e2eb..d1daddd7 100644 --- a/src/servers/Server_Zone/Session.cpp +++ b/src/servers/Server_Zone/Session.cpp @@ -9,6 +9,7 @@ Core::Session::Session( uint32_t sessionId ) : m_sessionId( sessionId ) + , m_isValid( false ) , m_lastDataTime( static_cast< uint32_t >( time( nullptr ) ) ) { @@ -50,7 +51,12 @@ bool Core::Session::loadPlayer() m_pPlayer = Entity::PlayerPtr( new Entity::Player() ); if( !m_pPlayer->load( m_sessionId, shared_from_this() ) ) + { + m_isValid = false; return false; + } + + m_isValid = true; return true; @@ -61,6 +67,9 @@ void Core::Session::close() if( m_pZoneConnection ) m_pZoneConnection->Disconnect(); + if( m_pChatConnection ) + m_pChatConnection->Disconnect(); + // remove the session from the player if( m_pPlayer ) // reset the zone, so the zone handler knows to remove the actor @@ -77,6 +86,11 @@ uint32_t Core::Session::getLastDataTime() const return m_lastDataTime; } +bool Core::Session::isValid() const +{ + return m_isValid; +} + void Core::Session::updateLastDataTime() { m_lastDataTime = static_cast< uint32_t >( time( nullptr ) ); diff --git a/src/servers/Server_Zone/Session.h b/src/servers/Server_Zone/Session.h index d1aba601..66b81090 100644 --- a/src/servers/Server_Zone/Session.h +++ b/src/servers/Server_Zone/Session.h @@ -35,6 +35,8 @@ namespace Core { void update(); + bool isValid() const; + Entity::PlayerPtr getPlayer() const; private: @@ -44,6 +46,8 @@ namespace Core { uint32_t m_lastDataTime; + bool m_isValid; + Network::GameConnectionPtr m_pZoneConnection; Network::GameConnectionPtr m_pChatConnection;