diff --git a/src/common/Network/PacketDef/Zone/ServerZoneDef.h b/src/common/Network/PacketDef/Zone/ServerZoneDef.h index d359ae6c..75ea7041 100644 --- a/src/common/Network/PacketDef/Zone/ServerZoneDef.h +++ b/src/common/Network/PacketDef/Zone/ServerZoneDef.h @@ -1681,7 +1681,7 @@ struct FFXIVIpcLandSetInitialize : FFXIVIpcBasePacket< LandSetInitialize > struct FFXIVIpcYardObjectSpawn : FFXIVIpcBasePacket { - uint8_t landSetId; + uint8_t landId; uint8_t objectArray; uint16_t unknown1; Common::YardObject object; diff --git a/src/world/Manager/HousingMgr.cpp b/src/world/Manager/HousingMgr.cpp index a9593901..0f481cd2 100644 --- a/src/world/Manager/HousingMgr.cpp +++ b/src/world/Manager/HousingMgr.cpp @@ -1038,7 +1038,7 @@ bool Sapphire::World::Manager::HousingMgr::placeInteriorItem( Entity::Player& pl { auto invMgr = g_fw.get< InventoryMgr >(); - auto containers = { + auto containerIds = { InventoryType::HousingInteriorPlacedItems1, InventoryType::HousingInteriorPlacedItems2, InventoryType::HousingInteriorPlacedItems3, @@ -1056,7 +1056,7 @@ bool Sapphire::World::Manager::HousingMgr::placeInteriorItem( Entity::Player& pl // find first free container uint8_t containerIdx = 0; - for( auto containerId : containers ) + for( auto containerId : containerIds ) { auto& container = getEstateInventory( ident )[ containerId ]; @@ -1079,9 +1079,24 @@ bool Sapphire::World::Manager::HousingMgr::placeInteriorItem( Entity::Player& pl invMgr->saveHousingContainer( ident, container ); invMgr->updateHousingItemPosition( item ); - break; + auto zone = std::dynamic_pointer_cast< Territory::Housing::HousingInteriorTerritory >( player.getCurrentZone() ); + assert( zone ); + + zone->spawnYardObject( containerIdx, freeSlot, item ); + + return true; } + return false; +} - return true; +Sapphire::Common::YardObject Sapphire::World::Manager::HousingMgr::getYardObjectForItem( Inventory::HousingItemPtr item ) const +{ + Sapphire::Common::YardObject obj {}; + + obj.pos = item->getPos(); + obj.itemRotation = item->getRot(); + obj.itemId = item->getAdditionalData(); + + return obj; } \ No newline at end of file diff --git a/src/world/Manager/HousingMgr.h b/src/world/Manager/HousingMgr.h index f9191c06..e7bc4251 100644 --- a/src/world/Manager/HousingMgr.h +++ b/src/world/Manager/HousingMgr.h @@ -149,6 +149,13 @@ namespace Sapphire::World::Manager void reqPlaceHousingItem( Entity::Player& player, uint16_t landId, uint16_t containerId, uint16_t slotId, Common::FFXIVARR_POSITION3 pos, float rotation ); + /*! + * @brief Returns the equivalent YardObject for a HousingItem + * @param item The item to convert into a YardObject + * @return The resultant YardObject + */ + Common::YardObject getYardObjectForItem( Inventory::HousingItemPtr item ) const; + private: /*! diff --git a/src/world/Territory/Housing/HousingInteriorTerritory.cpp b/src/world/Territory/Housing/HousingInteriorTerritory.cpp index f1044efa..a99daaed 100644 --- a/src/world/Territory/Housing/HousingInteriorTerritory.cpp +++ b/src/world/Territory/Housing/HousingInteriorTerritory.cpp @@ -13,6 +13,8 @@ #include "Manager/HousingMgr.h" #include "Territory/Land.h" #include "Territory/House.h" +#include "Inventory/ItemContainer.h" +#include "Inventory/HousingItem.h" #include "Forwards.h" #include "HousingInteriorTerritory.h" @@ -28,27 +30,27 @@ using namespace Sapphire::World::Manager; using namespace Sapphire; using namespace Sapphire::World::Territory; -Housing::HousingInteriorTerritory::HousingInteriorTerritory( Common::LandIdent ident, uint16_t territoryTypeId, - uint32_t guId, - const std::string& internalName, - const std::string& contentName ) : +Sapphire::World::Territory::Housing::HousingInteriorTerritory::HousingInteriorTerritory( Common::LandIdent ident, + uint16_t territoryTypeId, + uint32_t guId, + const std::string& internalName, + const std::string& contentName ) : Zone( territoryTypeId, guId, internalName, contentName ), m_landIdent( ident ) { m_lastActivityTime = static_cast< uint32_t >( Util::getTimeSeconds() ); } -Housing::HousingInteriorTerritory::~HousingInteriorTerritory() -{ +Housing::HousingInteriorTerritory::~HousingInteriorTerritory() = default; +bool Sapphire::World::Territory::Housing::HousingInteriorTerritory::init() +{ + updateYardObjects(); + + return true; } -bool Housing::HousingInteriorTerritory::init() -{ - return false; -} - -void Housing::HousingInteriorTerritory::onPlayerZoneIn( Entity::Player& player ) +void Sapphire::World::Territory::Housing::HousingInteriorTerritory::onPlayerZoneIn( Entity::Player& player ) { auto pHousingMgr = g_fw.get< HousingMgr >(); auto pLog = g_fw.get< Logger >(); @@ -56,7 +58,7 @@ void Housing::HousingInteriorTerritory::onPlayerZoneIn( Entity::Player& player ) "HousingInteriorTerritory::onPlayerZoneIn: Zone#" + std::to_string( getGuId() ) + "|" + std::to_string( getTerritoryTypeId() ) + ", Entity#" + std::to_string( player.getId() ) ); - auto indoorInitPacket = makeZonePacket< FFXIVIpcHousingIndoorInitialize >( player.getId() ); + auto indoorInitPacket = makeZonePacket< Server::FFXIVIpcHousingIndoorInitialize >( player.getId() ); indoorInitPacket->data().u1 = 0; indoorInitPacket->data().u2 = 0; indoorInitPacket->data().u3 = 0; @@ -72,40 +74,109 @@ void Housing::HousingInteriorTerritory::onPlayerZoneIn( Entity::Player& player ) static_cast< Common::HousingInteriorSlot >( i ) ); } - - uint32_t yardPacketNum; - uint32_t yardPacketTotal = 2 + pLand->getSize(); - player.queuePacket( indoorInitPacket ); - for( yardPacketNum = 0; yardPacketNum < yardPacketTotal; yardPacketNum++ ) + auto yardPacketTotal = static_cast< uint8_t >( 2 + pLand->getSize() ); + for( uint8_t yardPacketNum = 0; yardPacketNum < yardPacketTotal; yardPacketNum++ ) { - auto objectInitPacket = makeZonePacket< FFXIVIpcHousingObjectInitialize >( player.getId() ); + auto objectInitPacket = makeZonePacket< Server::FFXIVIpcHousingObjectInitialize >( player.getId() ); memcpy( &objectInitPacket->data().landIdent, &m_landIdent, sizeof( Common::LandIdent ) ); objectInitPacket->data().u1 = 0; objectInitPacket->data().u2 = 100; objectInitPacket->data().packetNum = yardPacketNum; objectInitPacket->data().packetTotal = yardPacketTotal; - //TODO: Add Objects here + auto yardObjectSize = sizeof( Common::YardObject ); + memcpy( &objectInitPacket->data().object, m_yardObjects.data() + ( yardPacketNum * 100 ), yardObjectSize * 100 ); player.queuePacket( objectInitPacket ); } } -void Housing::HousingInteriorTerritory::onUpdate( uint32_t currTime ) +void Sapphire::World::Territory::Housing::HousingInteriorTerritory::onUpdate( uint32_t currTime ) { if( m_playerMap.size() > 0 ) m_lastActivityTime = currTime; } -uint32_t Housing::HousingInteriorTerritory::getLastActivityTime() const +uint32_t Sapphire::World::Territory::Housing::HousingInteriorTerritory::getLastActivityTime() const { return m_lastActivityTime; } -const Common::LandIdent Housing::HousingInteriorTerritory::getLandIdent() const +const Common::LandIdent Sapphire::World::Territory::Housing::HousingInteriorTerritory::getLandIdent() const { return m_landIdent; +} + +void Sapphire::World::Territory::Housing::HousingInteriorTerritory::updateYardObjects() +{ + auto housingMgr = g_fw.get< Manager::HousingMgr >(); + + auto containerIds = { + InventoryType::HousingInteriorPlacedItems1, + InventoryType::HousingInteriorPlacedItems2, + InventoryType::HousingInteriorPlacedItems3, + InventoryType::HousingInteriorPlacedItems4, + InventoryType::HousingInteriorPlacedItems5, + InventoryType::HousingInteriorPlacedItems6, + InventoryType::HousingInteriorPlacedItems7, + InventoryType::HousingInteriorPlacedItems8, + }; + + // zero out the array + // there's some really weird behaviour where *some* values will cause the linkshell invite notification to pop up + // for some reason + Common::YardObject obj {}; + memset( &obj, 0x0, sizeof( Common::YardObject ) ); + m_yardObjects.fill( obj ); + + auto containers = housingMgr->getEstateInventory( getLandIdent() ); + + uint8_t containerIdx = 0; + for( auto containerId : containerIds ) + { + auto container = containers.find( containerId ); + if( container == containers.end() ) + // no more containers left + break; + + for( const auto& item : container->second->getItemMap() ) + { + auto housingItem = std::dynamic_pointer_cast< Inventory::HousingItem >( item.second ); + assert( housingItem ); + + auto offset = item.first + ( containerIdx * 50 ); + + auto obj = housingMgr->getYardObjectForItem( housingItem ); + + m_yardObjects[ offset ] = obj; + } + + containerIdx++; + } +} + +void Sapphire::World::Territory::Housing::HousingInteriorTerritory::spawnYardObject( uint8_t containerIdx, + uint16_t slot, + Inventory::HousingItemPtr item ) +{ + auto housingMgr = g_fw.get< Manager::HousingMgr >(); + + auto offset = ( containerIdx * 50 ) + slot; + auto obj = housingMgr->getYardObjectForItem( item ); + + m_yardObjects[ offset ] = obj; + +// for( const auto& player : m_playerMap ) +// { +// auto packet = makeZonePacket< Server::FFXIVIpcYardObjectSpawn >( player.second->getId() ); +// +// packet->data().landSetId = 0; +// packet->data().objectArray = static_cast< uint8_t >( offset ); +// packet->data().object = obj; +// +// player.second->queuePacket( packet ); +// } } \ No newline at end of file diff --git a/src/world/Territory/Housing/HousingInteriorTerritory.h b/src/world/Territory/Housing/HousingInteriorTerritory.h index 153e3604..12bcc233 100644 --- a/src/world/Territory/Housing/HousingInteriorTerritory.h +++ b/src/world/Territory/Housing/HousingInteriorTerritory.h @@ -1,5 +1,7 @@ #include "ForwardsZone.h" #include "Territory/Zone.h" +#include "Common.h" +#include namespace Sapphire::World::Territory::Housing { @@ -22,8 +24,13 @@ namespace Sapphire::World::Territory::Housing const Common::LandIdent getLandIdent() const; + void updateYardObjects(); + void spawnYardObject( uint8_t containerIdx, uint16_t slot, Inventory::HousingItemPtr item ); + private: Common::LandIdent m_landIdent; uint32_t m_lastActivityTime; + + std::array< Sapphire::Common::YardObject, 400 > m_yardObjects; }; } \ No newline at end of file diff --git a/src/world/Territory/HousingZone.cpp b/src/world/Territory/HousingZone.cpp index 414e927a..d7ff7e08 100644 --- a/src/world/Territory/HousingZone.cpp +++ b/src/world/Territory/HousingZone.cpp @@ -103,6 +103,14 @@ bool Sapphire::HousingZone::init() cursor += itemMax; } + // zero out the yard obj arrays so we don't leak memory like SE does :^) + Common::YardObject obj {}; + memset( &obj, 0x0, sizeof( Common::YardObject ) ); + + for( auto& arr : m_yardObjects ) + { + arr.fill( obj ); + } auto housingMgr = g_fw.get< World::Manager::HousingMgr >(); auto landCache = housingMgr->getLandCacheMap(); @@ -137,27 +145,7 @@ bool Sapphire::HousingZone::init() if( entry.m_houseId > 0 ) registerEstateEntranceEObj( entry.m_landId ); - // add items to yard object array - auto& inventory = housingMgr->getEstateInventory( land->getLandIdent() ); - auto& externalContainer = inventory[ InventoryType::HousingExteriorPlacedItems ]; - - auto arrayBounds = m_yardObjectArrayBounds[ entry.m_landId ]; - auto yardMapIndex = entry.m_landId <= 29 ? 0 : 1; - - for( auto& item : externalContainer->getItemMap() ) - { - Common::YardObject obj{}; - - auto housingItem = std::dynamic_pointer_cast< Inventory::HousingItem >( item.second ); - assert( housingItem ); - - obj.itemId = housingItem->getAdditionalData(); - obj.itemRotation = housingItem->getRot(); - obj.pos = housingItem->getPos(); - - auto idx = item.first + arrayBounds.first; - m_yardObjects[ yardMapIndex ][ idx ] = obj; - } + updateYardObjects( land->getLandIdent() ); } return true; @@ -344,9 +332,16 @@ void Sapphire::HousingZone::updateYardObjects( Sapphire::Common::LandIdent ident auto yardContainer = landStorage[ InventoryType::HousingExteriorPlacedItems ]; + auto arrayBounds = m_yardObjectArrayBounds[ ident.landId ]; + auto yardMapIndex = ident.landId <= 29 ? 0 : 1; + for( const auto& item : yardContainer->getItemMap() ) { + auto housingItem = std::dynamic_pointer_cast< Inventory::HousingItem >( item.second ); + assert( housingItem ); + auto idx = item.first + arrayBounds.first; + m_yardObjects[ yardMapIndex ][ idx ] = housingMgr->getYardObjectForItem( housingItem ); } } @@ -371,7 +366,7 @@ void Sapphire::HousingZone::spawnYardObject( uint8_t landId, uint16_t slotId, In { auto packet = makeZonePacket< Server::FFXIVIpcYardObjectSpawn >( player.second->getId() ); - packet->data().landSetId = landId; + packet->data().landId = landId; packet->data().objectArray = static_cast< uint8_t >( slotId ); packet->data().object = obj;