diff --git a/bin/config/config.ini.default b/bin/config/config.ini.default index 4c11b849..cacd9115 100644 --- a/bin/config/config.ini.default +++ b/bin/config/config.ini.default @@ -55,4 +55,8 @@ ListenPort = 54992 [General] ; Sent on login - each line must be shorter than 307 characters, split lines with ';' -MotD = Welcome to Sapphire!;This is a very good server;You can change these messages by editing General.MotD in config/zone.ini \ No newline at end of file +MotD = Welcome to Sapphire!;This is a very good server;You can change these messages by editing General.MotD in config/config.ini + +[Housing] +; Set the default estate name. %i will be replaced with the plot number +DefaultEstateName = Estate #%i \ No newline at end of file diff --git a/src/common/Database/ZoneDbConnection.cpp b/src/common/Database/ZoneDbConnection.cpp index b35ecf79..a19f32d9 100644 --- a/src/common/Database/ZoneDbConnection.cpp +++ b/src/common/Database/ZoneDbConnection.cpp @@ -197,11 +197,14 @@ void Sapphire::Db::ZoneDbConnection::doPrepareStatements() CONNECTION_BOTH ); prepareStatement( HOUSING_HOUSE_UP, - "UPDATE house SET BuildTime = ?, Aetheryte = ?, Comment = ?, HouseName = ?, Endorsements = ?, HousePartModels = ?, HousePartColours = ?, HouseInteriorModels = ? WHERE HouseId = ?;", + "UPDATE house SET BuildTime = ?, Aetheryte = ?, Comment = ?, HouseName = ?, Endorsements = ? WHERE HouseId = ?;", CONNECTION_BOTH ); prepareStatement( LAND_INV_SEL_ALL, - "SELECT LandIdent, ContainerId, ItemId, SlotId FROM houseiteminventory;", + "SELECT houseiteminventory.*, charaglobalitem.catalogId, charaglobalitem.stain, charaglobalitem.CharacterId " + "FROM houseiteminventory " + "LEFT JOIN charaglobalitem " + "ON houseiteminventory.ItemId = charaglobalitem.itemId;", CONNECTION_BOTH ); prepareStatement( LAND_INV_SEL_HOUSE, @@ -212,6 +215,13 @@ void Sapphire::Db::ZoneDbConnection::doPrepareStatements() "SELECT * FROM land WHERE LandSetId = ?;", CONNECTION_SYNC ); + prepareStatement( LAND_SEL_ALL, + "SELECT land.*, house.Welcome, house.Aetheryte, house.Comment, house.HouseName, house.BuildTime, house.Endorsements " + "FROM land " + "LEFT JOIN house " + "ON land.HouseId = house.HouseId;", + CONNECTION_SYNC ); + /*prepareStatement( LAND_INS, "INSERT INTO land ( LandSetId ) VALUES ( ? );", CONNECTION_BOTH ); diff --git a/src/common/Database/ZoneDbConnection.h b/src/common/Database/ZoneDbConnection.h index 2cbfac6e..179993d9 100644 --- a/src/common/Database/ZoneDbConnection.h +++ b/src/common/Database/ZoneDbConnection.h @@ -80,6 +80,7 @@ namespace Sapphire::Db LAND_INS, LAND_SEL, + LAND_SEL_ALL, LAND_UP, LANDSET_SEL, HOUSING_HOUSE_INS, diff --git a/src/world/Manager/HousingMgr.cpp b/src/world/Manager/HousingMgr.cpp index 44846d0e..50138589 100644 --- a/src/world/Manager/HousingMgr.cpp +++ b/src/world/Manager/HousingMgr.cpp @@ -25,6 +25,8 @@ #include "ServerMgr.h" #include "Territory/House.h" #include "InventoryMgr.h" +#include "Inventory/Item.h" +#include "Inventory/ItemContainer.h" using namespace Sapphire::Common; using namespace Sapphire::Network; @@ -33,22 +35,133 @@ using namespace Sapphire::Network::Packets::Server; extern Sapphire::Framework g_fw; -Sapphire::World::Manager::HousingMgr::HousingMgr() -{ - -} - -Sapphire::World::Manager::HousingMgr::~HousingMgr() -{ - -} +Sapphire::World::Manager::HousingMgr::HousingMgr() = default; +Sapphire::World::Manager::HousingMgr::~HousingMgr() = default; bool Sapphire::World::Manager::HousingMgr::init() { + auto log = g_fw.get< Sapphire::Logger >(); + + log->info( "HousingMgr: Caching housing land init data" ); + //LAND_SEL_ALL + + // 18 wards per territory, 4 territories + m_landCache.reserve( 18 * 4 ); + + loadLandCache(); + + log->debug( "HousingMgr: Checking land counts" ); + + uint32_t houseCount = 0; + for( auto& landSet : m_landCache ) + { + auto count = landSet.second.size(); + + houseCount += count; + + if( landSet.second.size() != 60 ) + { + log->fatal( "LandSet " + std::to_string( landSet.first ) + " is missing land entries. Only have " + std::to_string( count ) + " land entries." ); + return false; + } + } + + log->info( "HousingMgr: Cached " + std::to_string( houseCount ) + " houses" ); + + ///// + + if( !loadEstateInventories() ) + return false; return true; } +bool Sapphire::World::Manager::HousingMgr::loadEstateInventories() +{ + auto log = g_fw.get< Sapphire::Logger >(); + + log->info( "HousingMgr: Loading inventories for estates" ); + + auto pDb = g_fw.get< Db::DbWorkerPool< Db::ZoneDbConnection > >(); + + auto stmt = pDb->getPreparedStatement( Db::LAND_INV_SEL_ALL ); + auto res = pDb->query( stmt ); + + uint32_t itemCount = 0; + while( res->next() ) + { + //uint64_t uId, uint32_t catalogId, uint64_t model1, uint64_t model2, bool isHq + uint64_t ident = res->getUInt64( "LandIdent" ); + uint16_t containerId = res->getUInt16( "ContainerId" ); + uint64_t itemId = res->getUInt64( "ItemId" ); + uint16_t slot = res->getUInt16( "SlotId" ); + uint32_t catalogId = res->getUInt( "catalogId" ); + uint8_t stain = res->getUInt8( "stain" ); + uint64_t characterId = res->getUInt64( "CharacterId" ); + + auto item = make_Item( itemId, catalogId, 0, 0, 0 ); + item->setStain( stain ); + // todo: need to set the owner character id on the item + + ContainerIdToContainerMap& estateInv = m_estateInventories[ ident ]; + + // check if containerId exists + auto container = estateInv.find( containerId ); + if( container == estateInv.end() ) + { + // create container + // todo: how to handle this max slot stuff? override it on land init? + auto ic = make_ItemContainer( containerId, 400, "houseiteminventory", true ); + ic->setItem( slot, item ); + + estateInv[ containerId ] = ic; + } + else + container->second->setItem( slot, item ); + + itemCount++; + } + + log->debug( "HousingMgr: Loaded " + std::to_string( itemCount ) + " inventory items" ); + + return true; +} + +void Sapphire::World::Manager::HousingMgr::loadLandCache() +{ + auto pDb = g_fw.get< Db::DbWorkerPool< Db::ZoneDbConnection > >(); + + auto stmt = pDb->getPreparedStatement( Db::LAND_SEL_ALL ); + auto res = pDb->query( stmt ); + + while( res->next() ) + { + LandCacheEntry entry; + + // land stuff + entry.m_landSetId = res->getUInt64( "LandSetId" ); + entry.m_landId = res->getUInt64( "LandId" ); + + entry.m_type = static_cast< Common::LandType >( res->getUInt( "Type" ) ); + entry.m_size = res->getUInt8( "Size" ); + entry.m_status = res->getUInt8( "Status" ); + entry.m_currentPrice = res->getUInt64( "LandPrice" ); + entry.m_updateTime = res->getUInt64( "UpdateTime" ); + entry.m_ownerId = res->getUInt64( "OwnerId" ); + + entry.m_houseId = res->getUInt64( "HouseId" ); + + // house stuff + entry.m_estateWelcome = res->getString( "Welcome" ); + entry.m_estateComment = res->getString( "Comment" ); + entry.m_estateName = res->getString( "HouseName" ); + entry.m_buildTime = res->getUInt64( "BuildTime" ); + entry.m_endorsements = res->getUInt64( "Endorsements" ); + + m_landCache[ entry.m_landSetId ].push_back( entry ); + } +} + uint32_t Sapphire::World::Manager::HousingMgr::toLandSetId( uint16_t territoryTypeId, uint8_t wardId ) const { return ( static_cast< uint32_t >( territoryTypeId ) << 16 ) | wardId; @@ -472,7 +585,8 @@ Sapphire::Common::LandIdent Sapphire::World::Manager::HousingMgr::clientTriggerP return ident; } -void Sapphire::World::Manager::HousingMgr::sendHousingInventory( Entity::Player& player, uint16_t inventoryType, uint8_t plotNum ) +void Sapphire::World::Manager::HousingMgr::sendEstateInventory( Entity::Player& player, uint16_t inventoryType, + uint8_t plotNum ) { Sapphire::LandPtr targetLand; @@ -510,10 +624,72 @@ void Sapphire::World::Manager::HousingMgr::sendHousingInventory( Entity::Player& if( targetLand->getOwnerId() != player.getId() ) return; - auto container = targetLand->getItemContainer( inventoryType ); + auto container = getEstateInventory( targetLand->getLandIdent() )[ inventoryType ]; if( !container ) return; auto invMgr = g_fw.get< Manager::InventoryMgr >(); invMgr->sendInventoryContainer( player, container ); -} \ No newline at end of file +} + +const Sapphire::World::Manager::HousingMgr::LandSetLandCacheMap& + Sapphire::World::Manager::HousingMgr::getLandCacheMap() +{ + return m_landCache; +} + +Sapphire::World::Manager::HousingMgr::LandIdentToInventoryContainerMap& + Sapphire::World::Manager::HousingMgr::getEstateInventories() +{ + return m_estateInventories; +} + +Sapphire::World::Manager::HousingMgr::ContainerIdToContainerMap& + Sapphire::World::Manager::HousingMgr::getEstateInventory( uint64_t ident ) +{ + auto map = m_estateInventories.find( ident ); + + assert( map != m_estateInventories.end() ); + + return map->second; +} + +Sapphire::World::Manager::HousingMgr::ContainerIdToContainerMap& + Sapphire::World::Manager::HousingMgr::getEstateInventory( Sapphire::Common::LandIdent ident ) +{ + auto u64ident = *reinterpret_cast< uint64_t* >( &ident ); + + return getEstateInventory( u64ident ); +} + +void Sapphire::World::Manager::HousingMgr::updateHouseModels( Sapphire::HousePtr house ) +{ + assert( house ); + + auto getItemData = []( uint32_t itemId ) + { + auto pExdData = g_fw.get< Data::ExdDataGenerated >(); + auto info = pExdData->get< Sapphire::Data::Item >( itemId ); + return info->additionalData; + }; + + auto containers = getEstateInventory( house->getLandIdent() ); + + auto extContainer = containers.find( static_cast< uint16_t >( InventoryType::HousingOutdoorAppearance ) ); + if( extContainer != containers.end() ) + { + for( auto& item : extContainer->second->getItemMap() ) + { + house->setHousePart( static_cast< Common::HousePartSlot >( item.first ), getItemData( item.second->getId() ) ); + } + } + + auto intContainer = containers.find( static_cast< uint16_t >( InventoryType::HousingInteriorAppearance ) ); + if( intContainer != containers.end() ) + { + for( auto& item : intContainer->second->getItemMap() ) + { + house->setHouseInteriorPart( static_cast< Common::HousingInteriorSlot >( item.first ), getItemData( item.second->getId() ) ); + } + } +} diff --git a/src/world/Manager/HousingMgr.h b/src/world/Manager/HousingMgr.h index b4334714..d0c59a6c 100644 --- a/src/world/Manager/HousingMgr.h +++ b/src/world/Manager/HousingMgr.h @@ -17,6 +17,47 @@ namespace Sapphire::World::Manager { public: + + struct LandCacheEntry + { + // land table + uint64_t m_landSetId; + uint64_t m_landId; + + Common::LandType m_type; + uint8_t m_size; + uint8_t m_status; + + uint64_t m_currentPrice; + + uint64_t m_updateTime; + uint64_t m_ownerId; + uint64_t m_houseId; + + // house table + + std::string m_estateWelcome; + std::string m_estateComment; + std::string m_estateName; + + uint64_t m_buildTime; + uint64_t m_endorsements; + }; + + /*! + * @brief Maps land id to a list of cached entries + */ + using LandSetLandCacheMap = std::unordered_map< uint64_t, std::vector< LandCacheEntry > >; + + /*! + * @brief Maps container IDs to their relevant ItemContainerPtr + */ + using ContainerIdToContainerMap = std::unordered_map< uint16_t, ItemContainerPtr >; + /*! + * @brief Maps land idents to a container containing ItemContainerPtrs + */ + using LandIdentToInventoryContainerMap = std::unordered_map< uint64_t, ContainerIdToContainerMap >; + HousingMgr(); virtual ~HousingMgr(); @@ -50,12 +91,52 @@ namespace Sapphire::World::Manager void sendEstateGreeting( Entity::Player& player, const Common::LandIdent ident ); + /*! + * @brief Updates the cached models on a house from the relevant apperance inventories. + * Does not send the subsequent update to clients. + * + * @param house The house to update the models for + */ + void updateHouseModels( HousePtr house ); + /*! * @brief Sends the house inventory for the specified type to a player. * - * This enforces permissions on the inventory too so random players can't request a houses items + * This enforces permissions on the inventory too so random players can't request an estates items */ - void sendHousingInventory( Entity::Player& player, uint16_t inventoryType, uint8_t plotNum ); + void sendEstateInventory( Entity::Player& player, uint16_t inventoryType, uint8_t plotNum ); + + /*! + * @brief Get the land & house data that was cached on world startup. + * @return + */ + const LandSetLandCacheMap& getLandCacheMap(); + + /*! + * @brief Get all loaded inventories for housing estates + * @return + */ + LandIdentToInventoryContainerMap& getEstateInventories(); + + /*! + * @brief Get an estate inventory for a specific estate + * @param ident LandIdent for the specified estate + * @return A map containing container ids to ItemContainerPtr + */ + ContainerIdToContainerMap& getEstateInventory( uint64_t ident ); + + /*! + * @brief Get an estate inventory for a specific estate + * @param ident LandIdent for the specified estate + * @return A map containing container ids to ItemContainerPtr + */ + ContainerIdToContainerMap& getEstateInventory( Common::LandIdent ident ); + private: + void loadLandCache(); + bool loadEstateInventories(); + + LandSetLandCacheMap m_landCache; + LandIdentToInventoryContainerMap m_estateInventories; }; diff --git a/src/world/Network/Handlers/ClientTriggerHandler.cpp b/src/world/Network/Handlers/ClientTriggerHandler.cpp index de7528c2..e61e168d 100644 --- a/src/world/Network/Handlers/ClientTriggerHandler.cpp +++ b/src/world/Network/Handlers/ClientTriggerHandler.cpp @@ -434,7 +434,7 @@ void Sapphire::Network::GameConnection::clientTriggerHandler( const Packets::FFX if( param2 == 1 ) inventoryType = Common::InventoryType::HousingOutdoorStoreroom; - housingMgr->sendHousingInventory( player, inventoryType, plot ); + housingMgr->sendEstateInventory( player, inventoryType, plot ); break; } diff --git a/src/world/ServerMgr.cpp b/src/world/ServerMgr.cpp index ec597356..842151d3 100644 --- a/src/world/ServerMgr.cpp +++ b/src/world/ServerMgr.cpp @@ -151,6 +151,12 @@ void Sapphire::ServerMgr::run( int32_t argc, char* argv[] ) auto pTeriMgr = std::make_shared< Manager::TerritoryMgr >(); auto pHousingMgr = std::make_shared< Manager::HousingMgr >(); g_fw.set< Manager::HousingMgr >( pHousingMgr ); + if( !pHousingMgr->init() ) + { + pLog->fatal( "Failed to setup housing!" ); + return; + } + g_fw.set< Manager::TerritoryMgr >( pTeriMgr ); if( !pTeriMgr->init() ) { diff --git a/src/world/Territory/House.cpp b/src/world/Territory/House.cpp index 820a7b1e..404f9f47 100644 --- a/src/world/Territory/House.cpp +++ b/src/world/Territory/House.cpp @@ -13,104 +13,35 @@ extern Sapphire::Framework g_fw; -Sapphire::House::House( uint32_t houseId, uint32_t landSetId, Common::LandIdent ident ) : +Sapphire::House::House( uint32_t houseId, uint32_t landSetId, Common::LandIdent ident, const std::string& estateName, + const std::string& estateComment ) : m_houseId( houseId ), m_landSetId( landSetId ), - m_landIdent( ident ) + m_landIdent( ident ), + m_estateName( estateName ), + m_estateComment( estateComment ) { - auto pDB = g_fw.get< Db::DbWorkerPool< Db::ZoneDbConnection > >(); - // todo: convert to prepared statement - auto res = pDB->query( "SELECT * FROM house WHERE HouseId = " + std::to_string( houseId ) ); - - if( !res->next() ) - { - g_fw.get< Sapphire::Logger >()->info( "Creating house House#" + std::to_string( houseId ) + " in LandSet#" + std::to_string( landSetId ) ); - - auto stmt = pDB->getPreparedStatement( Db::HOUSING_HOUSE_INS ); - - stmt->setUInt( 1, m_landSetId ); - stmt->setUInt( 2, m_houseId ); - - pDB->execute( stmt ); - - // todo: make this nicer/configurable? - m_houseName = "Estate #" + std::to_string( m_landIdent.landId + 1 ); - } - else - { - m_estateMessage = res->getString( "Comment" ); - m_houseName = res->getString( "HouseName" ); - - auto housePartModels = res->getBlobVector( "HousePartModels" ); - auto housePartColours = res->getBlobVector( "HousePartColours" ); - - auto models = reinterpret_cast< uint32_t* >( &housePartModels[ 0 ] ); - for( auto i = 0; i < 8; i++ ) - { - m_houseParts[ i ] = { models[ i ], housePartColours[ i ] }; - } - - auto houseInteriorModels = res->getBlobVector( "HouseInteriorModels" ); - - auto interiorModels = reinterpret_cast< uint32_t* >( &houseInteriorModels[ 0 ] ); - for( auto i = 0; i < 10; i++ ) - { - m_houseInteriorParts[ i ] = interiorModels[ i ]; - } - } } -Sapphire::House::~House() -{ -} +Sapphire::House::~House() = default; void Sapphire::House::updateHouseDb() { auto pDB = g_fw.get< Db::DbWorkerPool< Db::ZoneDbConnection > >(); - // BuildTime = 1, Aetheryte = 2, Comment = 3, HouseName = 4, Endorsements = 5, - // HousePartModels = 6, HousePartColours = 7, HouseId = 8 + // BuildTime = 1, Aetheryte = 2, Comment = 3, HouseName = 4, Endorsements = 5, HouseId = 6 auto stmt = pDB->getPreparedStatement( Db::HOUSING_HOUSE_UP ); - stmt->setUInt( 9, m_houseId ); + stmt->setUInt( 6, m_houseId ); stmt->setInt64( 1, m_buildTime ); stmt->setInt( 2, 0 ); - stmt->setString( 3, m_estateMessage ); - stmt->setString( 4, m_houseName ); + stmt->setString( 3, m_estateComment ); + stmt->setString( 4, m_estateName ); stmt->setUInt64( 5, 0 ); - std::vector< uint32_t > models; - std::vector< uint8_t > colours; - - for( auto i = 0; i < 8; i++ ) - { - auto& part = m_houseParts[ i ]; - models.push_back( part.first ); - colours.push_back( part.second ); - } - - // todo: this is shit - std::vector< uint8_t > tmpModels( models.size() * 4 ); - memcpy( tmpModels.data(), models.data(), tmpModels.size() ); - - stmt->setBinary( 6, tmpModels ); - stmt->setBinary( 7, colours ); - - models.clear(); - - for( auto i = 0; i < 10; i++ ) - { - models.push_back( m_houseInteriorParts[ i ] ); - } - - std::vector< uint8_t > tmp2Models( models.size() * 4 ); - memcpy( tmp2Models.data(), models.data(), tmp2Models.size() ); - - stmt->setBinary( 8, tmp2Models ); - pDB->execute( stmt ); } @@ -131,59 +62,59 @@ uint32_t Sapphire::House::getHouseId() const uint8_t Sapphire::House::getHousePartColor( Common::HousePartSlot slot ) const { - return m_houseParts[ slot ].second; + return m_houseModelsCache[ slot ].second; } uint32_t Sapphire::House::getHouseInteriorPart( Common::HousingInteriorSlot slot ) const { - return m_houseInteriorParts[ slot ]; + return m_houseInteriorModels[ slot ]; } void Sapphire::House::setHousePart( Common::HousePartSlot slot, uint32_t id ) { - m_houseParts[ slot ].first = id; + m_houseModelsCache[ slot ].first = id; } void Sapphire::House::setHousePartColor( Common::HousePartSlot slot, uint32_t id ) { - m_houseParts[ slot ].second = id; + m_houseModelsCache[ slot ].second = id; } void Sapphire::House::setHouseInteriorPart( Common::HousingInteriorSlot slot, uint32_t id ) { - m_houseInteriorParts[ slot ] = id; + m_houseInteriorModels[ slot ] = id; } uint32_t Sapphire::House::getHousePart( Common::HousePartSlot slot ) const { - return m_houseParts[ slot ].first; + return m_houseModelsCache[ slot ].first; } -Sapphire::House::HousePartsArray const& Sapphire::House::getHouseParts() const +Sapphire::House::HouseModelsArray const& Sapphire::House::getHouseModels() const { - return m_houseParts; + return m_houseModelsCache; } const std::string& Sapphire::House::getHouseName() const { - return m_houseName; + return m_estateName; } const std::string& Sapphire::House::getHouseGreeting() const { - return m_estateMessage; + return m_estateComment; } void Sapphire::House::setHouseGreeting( const std::string& greeting ) { - m_estateMessage = greeting; + m_estateComment = greeting; updateHouseDb(); } void Sapphire::House::setHouseName( const std::string& name ) { - m_houseName = name; + m_estateName = name; updateHouseDb(); } \ No newline at end of file diff --git a/src/world/Territory/House.h b/src/world/Territory/House.h index f228874b..f4c71817 100644 --- a/src/world/Territory/House.h +++ b/src/world/Territory/House.h @@ -12,11 +12,12 @@ namespace Sapphire class House { public: - House( uint32_t houseId, uint32_t landSetId, Common::LandIdent ident ); + House( uint32_t houseId, uint32_t landSetId, Common::LandIdent ident, const std::string& estateName, + const std::string& estateComment ); virtual ~House(); using HousePart = std::pair< uint32_t, uint8_t >; - using HousePartsArray = std::array< HousePart, 8 >; + using HouseModelsArray = std::array< HousePart, 8 >; //gerneral uint32_t getLandSetId() const; @@ -37,7 +38,7 @@ namespace Sapphire uint8_t getHousePartColor( Common::HousePartSlot slot ) const; uint32_t getHouseInteriorPart( Common::HousingInteriorSlot slot ) const; - HousePartsArray const& getHouseParts() const; + HouseModelsArray const& getHouseModels() const; void updateHouseDb(); @@ -48,11 +49,11 @@ namespace Sapphire uint64_t m_buildTime; - HousePartsArray m_houseParts; - uint32_t m_houseInteriorParts[10]; + HouseModelsArray m_houseModelsCache; + uint32_t m_houseInteriorModels[10]; - std::string m_estateMessage; - std::string m_houseName; + std::string m_estateComment; + std::string m_estateName; }; } diff --git a/src/world/Territory/HousingZone.cpp b/src/world/Territory/HousingZone.cpp index 17fdfd3b..075059e2 100644 --- a/src/world/Territory/HousingZone.cpp +++ b/src/world/Territory/HousingZone.cpp @@ -64,40 +64,37 @@ bool Sapphire::HousingZone::init() auto pExdData = g_fw.get< Data::ExdDataGenerated >(); auto info = pExdData->get< Sapphire::Data::HousingLandSet >( housingIndex ); - auto stmt = pDb->getPreparedStatement( Db::LANDSET_SEL ); - stmt->setUInt64( 1, m_landSetId ); - auto res = pDb->query( stmt ); + auto housingMgr = g_fw.get< World::Manager::HousingMgr >(); + auto landCache = housingMgr->getLandCacheMap(); - std::vector< QueuedLandInit > landInit; - - while( res->next() ) + // make sure the landset exists + auto landSetCache = landCache.find( m_landSetId ); + if( landSetCache == landCache.end() ) { - - QueuedLandInit init; - init.m_landId = res->getUInt64( "LandId" ); - init.m_type = static_cast< Common::LandType >( res->getUInt( "Type" ) ); - init.m_size = res->getUInt( "Size" ); - init.m_status = res->getUInt( "Status" ); - init.m_currentPrice = res->getUInt( "LandPrice" ); - init.m_ownerId = res->getUInt64( "OwnerId" ); - init.m_houseId = res->getUInt64( "HouseId" ); - - landInit.push_back( init ); + g_fw.get< Sapphire::Logger >()->fatal( "LandSet " + std::to_string( m_landSetId ) + " is missing from the land cache." ); + return false; } - // nuke current query connection so the queries still in land don't fail - res.reset(); - - // spawn land - for( auto& init : landInit ) + // init the lands + for( HousingMgr::LandCacheEntry& entry : landSetCache->second ) { - auto land = make_Land( m_territoryTypeId, getWardNum(), init.m_landId, m_landSetId, info ); - land->init( init.m_type, init.m_size, init.m_status, init.m_currentPrice, init.m_ownerId, init.m_houseId ); + auto land = make_Land( m_territoryTypeId, getWardNum(), entry.m_landId, m_landSetId, info ); - m_landPtrMap[ init.m_landId ] = land; + // setup house + if( entry.m_houseId ) + { + auto house = make_House( entry.m_houseId, m_landSetId, land->getLandIdent(), entry.m_estateName, entry.m_estateComment ); - if( init.m_houseId > 0 ) - registerHouseEntranceEObj( init.m_landId ); + housingMgr->updateHouseModels( house ); + land->setHouse( house ); + } + + land->init( entry.m_type, entry.m_size, entry.m_status, entry.m_currentPrice, entry.m_ownerId, entry.m_houseId ); + + m_landPtrMap[ entry.m_landId ] = land; + + if( entry.m_houseId > 0 ) + registerHouseEntranceEObj( entry.m_landId ); } return true; @@ -119,20 +116,20 @@ void Sapphire::HousingZone::onPlayerZoneIn( Entity::Player& player ) for( yardPacketNum = 0; yardPacketNum < yardPacketTotal; yardPacketNum++ ) { - auto housingObjectInitializPacket = makeZonePacket< FFXIVIpcHousingObjectInitialize >( player.getId() ); - memset( &housingObjectInitializPacket->data().landIdent, 0xFF, sizeof( Common::LandIdent ) ); - housingObjectInitializPacket->data().u1 = 0xFF; - housingObjectInitializPacket->data().packetNum = yardPacketNum; - housingObjectInitializPacket->data().packetTotal = yardPacketTotal; + auto housingObjectInit = makeZonePacket< FFXIVIpcHousingObjectInitialize >( player.getId() ); + memset( &housingObjectInit->data().landIdent, 0xFF, sizeof( Common::LandIdent ) ); + housingObjectInit->data().u1 = 0xFF; + housingObjectInit->data().packetNum = yardPacketNum; + housingObjectInit->data().packetTotal = yardPacketTotal; //TODO: Add Objects here - player.queuePacket( housingObjectInitializPacket ); + player.queuePacket( housingObjectInit ); } auto landSetMap = makeZonePacket< FFXIVIpcLandSetMap >( player.getId() ); - landSetMap->data().subdivision = isPlayerSubInstance( player ) == false ? 2 : 1; - uint8_t startIndex = isPlayerSubInstance( player ) == false ? 0 : 30; + landSetMap->data().subdivision = !isPlayerSubInstance( player ) ? 2 : 1; + uint8_t startIndex = !isPlayerSubInstance( player ) ? 0 : 30; for( uint8_t i = startIndex, count = 0; i < ( startIndex + 30 ); i++, count++ ) { landSetMap->data().landInfo[ count ].status = 1; @@ -151,9 +148,9 @@ void Sapphire::HousingZone::sendLandSet( Entity::Player& player ) landsetInitializePacket->data().landIdent.territoryTypeId = m_territoryTypeId; //TODO: get current WorldId landsetInitializePacket->data().landIdent.worldId = 67; - landsetInitializePacket->data().subInstance = isPlayerSubInstance( player ) == false ? 1 : 2; + landsetInitializePacket->data().subInstance = !isPlayerSubInstance( player ) ? 1 : 2; - uint8_t startIndex = isPlayerSubInstance( player ) == false ? 0 : 30; + uint8_t startIndex = !isPlayerSubInstance( player ) ? 0 : 30; for( uint8_t i = startIndex, count = 0; i < ( startIndex + 30 ); ++i, ++count ) { @@ -173,7 +170,7 @@ void Sapphire::HousingZone::sendLandSet( Entity::Player& player ) { landData.flags = 1; - auto& parts = house->getHouseParts(); + auto& parts = house->getHouseModels(); for( auto i = 0; i != parts.size(); i++ ) { @@ -210,7 +207,7 @@ void Sapphire::HousingZone::sendLandUpdate( uint8_t landId ) { landData.flags = 1; - auto& parts = house->getHouseParts(); + auto& parts = house->getHouseModels(); for( auto i = 0; i != parts.size(); i++ ) { diff --git a/src/world/Territory/HousingZone.h b/src/world/Territory/HousingZone.h index 63fcfb4b..3868f31a 100644 --- a/src/world/Territory/HousingZone.h +++ b/src/world/Territory/HousingZone.h @@ -53,17 +53,6 @@ namespace Sapphire Entity::EventObjectPtr registerHouseEntranceEObj( uint8_t plotId ); private: - struct QueuedLandInit - { - uint64_t m_landId; - Common::LandType m_type; - uint8_t m_size; - uint8_t m_status; - uint32_t m_currentPrice; - uint64_t m_ownerId; - uint64_t m_houseId; - }; - using LandPtrMap = std::unordered_map< uint8_t, Sapphire::LandPtr >; const uint32_t m_landSetMax = 18; LandPtrMap m_landPtrMap; diff --git a/src/world/Territory/Land.cpp b/src/world/Territory/Land.cpp index d575cfea..4a1d075a 100644 --- a/src/world/Territory/Land.cpp +++ b/src/world/Territory/Land.cpp @@ -62,10 +62,6 @@ void Sapphire::Land::init( Common::LandType type, uint8_t size, uint8_t state, u m_currentPrice = currentPrice; m_ownerId = ownerId; - // fetch the house if we have one for this land - if( houseId > 0 ) - m_pHouse = make_House( houseId, m_landSetId, getLandIdent() ); - auto pExdData = g_fw.get< Data::ExdDataGenerated >(); auto info = pExdData->get< Sapphire::Data::HousingMapMarkerInfo >( m_landIdent.territoryTypeId, m_landIdent.landId ); if( info ) @@ -94,65 +90,23 @@ void Sapphire::Land::init( Common::LandType type, uint8_t size, uint8_t state, u } // init item containers - auto setupContainer = [ this ]( InventoryType type, uint16_t maxSize ) - { - m_landInventoryMap[ type ] = make_ItemContainer( type, maxSize, "houseiteminventory", true, true ); - }; - - setupContainer( InventoryType::HousingOutdoorAppearance, 8 ); - setupContainer( InventoryType::HousingOutdoorPlacedItems, m_maxPlacedExternalItems ); - setupContainer( InventoryType::HousingOutdoorStoreroom, m_maxPlacedExternalItems ); - - setupContainer( InventoryType::HousingInteriorAppearance, 9 ); - - // nb: so we're going to store these internally in one container because SE is fucked in the head - // but when an inventory is requested, we will split them into groups of 50 - setupContainer( InventoryType::HousingInteriorPlacedItems1, m_maxPlacedInternalItems ); - setupContainer( InventoryType::HousingInteriorStoreroom1, m_maxPlacedInternalItems ); - - loadItemContainerContents(); -} - -void Sapphire::Land::loadItemContainerContents() -{ - if( !m_pHouse ) - return; - - auto ident = *reinterpret_cast< uint64_t* >( &m_landIdent ); - g_fw.get< Sapphire::Logger >()->debug( "Loading housing inventory for ident: " + std::to_string( ident ) ); - - auto pDB = g_fw.get< Db::DbWorkerPool< Db::ZoneDbConnection > >(); - - auto stmt = pDB->getPreparedStatement( Db::LAND_INV_SEL_HOUSE ); - stmt->setUInt64( 1, ident ); - - auto res = pDB->query( stmt ); - - std::unordered_map< uint16_t, std::vector< std::pair< uint16_t, uint32_t > > > items; - - while( res->next() ) - { - auto containerId = res->getUInt( "ContainerId" ); - auto itemId = res->getUInt64( "ItemId" ); - auto slotId = res->getUInt( "SlotId" ); - - items[ containerId ].push_back( std::make_pair( slotId, itemId ) ); - } - - res.reset(); - - for( auto it = items.begin(); it != items.end(); it++ ) - { - auto container = m_landInventoryMap[ it->first ]; - - // todo: delet this - for( auto fuck = it->second.begin(); fuck != it->second.end(); fuck++ ) - { - auto item = Sapphire::Items::Util::loadItem( fuck->second ); - if( item ) - container->setItem( fuck->first, item ); - } - } +// auto setupContainer = [ this ]( InventoryType type, uint16_t maxSize ) +// { +// m_landInventoryMap[ type ] = make_ItemContainer( type, maxSize, "houseiteminventory", true, true ); +// }; +// +// setupContainer( InventoryType::HousingOutdoorAppearance, 8 ); +// setupContainer( InventoryType::HousingOutdoorPlacedItems, m_maxPlacedExternalItems ); +// setupContainer( InventoryType::HousingOutdoorStoreroom, m_maxPlacedExternalItems ); +// +// setupContainer( InventoryType::HousingInteriorAppearance, 9 ); +// +// // nb: so we're going to store these internally in one container because SE is fucked in the head +// // but when an inventory is requested, we will split them into groups of 50 +// setupContainer( InventoryType::HousingInteriorPlacedItems1, m_maxPlacedInternalItems ); +// setupContainer( InventoryType::HousingInteriorStoreroom1, m_maxPlacedInternalItems ); +// +// loadItemContainerContents(); } uint32_t Sapphire::Land::convertItemIdToHousingItemId( uint32_t itemId ) @@ -223,6 +177,11 @@ Sapphire::HousePtr Sapphire::Land::getHouse() const return m_pHouse; } +void Sapphire::Land::setHouse( Sapphire::HousePtr house ) +{ + m_pHouse = house; +} + FFXIVARR_POSITION3 Sapphire::Land::getMapMarkerPosition() { return m_mapMarkerPosition; @@ -349,7 +308,8 @@ bool Sapphire::Land::setPreset( uint32_t itemId ) { // todo: i guess we'd create a house here? auto newId = getNextHouseId(); - m_pHouse = make_House( newId, getLandSetId(), getLandIdent() ); + + m_pHouse = make_House( newId, getLandSetId(), getLandIdent(), "Estate #" + std::to_string( m_landIdent.landId + 1 ), "" ); } @@ -371,13 +331,4 @@ bool Sapphire::Land::setPreset( uint32_t itemId ) return true; -} - -Sapphire::ItemContainerPtr Sapphire::Land::getItemContainer( uint16_t inventoryType ) const -{ - auto container = m_landInventoryMap.find( inventoryType ); - if( container == m_landInventoryMap.end() ) - return nullptr; - - return container->second; } \ No newline at end of file diff --git a/src/world/Territory/Land.h b/src/world/Territory/Land.h index bf8a834c..19977e77 100644 --- a/src/world/Territory/Land.h +++ b/src/world/Territory/Land.h @@ -33,6 +33,7 @@ namespace Sapphire uint8_t getSharing() const; uint32_t getLandSetId() const; Common::LandType getLandType() const; + void setHouse( Sapphire::HousePtr ); Sapphire::HousePtr getHouse() const; Common::FFXIVARR_POSITION3 getMapMarkerPosition(); Common::LandIdent getLandIdent() const; @@ -60,9 +61,6 @@ namespace Sapphire void setLandTag( uint8_t slot, uint8_t tag ); uint8_t getLandTag( uint8_t slot ); - ItemContainerPtr getItemContainer( uint16_t inventoryType ) const; - void loadItemContainerContents(); - private: uint32_t convertItemIdToHousingItemId( uint32_t itemId ); uint32_t getNextHouseId(); @@ -86,7 +84,6 @@ namespace Sapphire Sapphire::HousePtr m_pHouse; //item storage - LandInventoryMap m_landInventoryMap; uint16_t m_maxPlacedExternalItems; uint16_t m_maxPlacedInternalItems;