diff --git a/.travis.yml b/.travis.yml index 3df10328..8ca1bc43 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,9 +13,7 @@ matrix: apt: sources: - ubuntu-toolchain-r-test - - llvm-toolchain-trusty-6.0 packages: - - clang-6.0 - g++-7 env: - MATRIX_EVAL="CC=gcc-7 && CXX=g++-7" @@ -29,8 +27,7 @@ cache: # Setup build matrix and dependencies before_install: - eval "${MATRIX_EVAL}" - - gem install --no-ri --no-rdoc mtime_cache - - sudo add-apt-repository -y ppa:rexut/recoil + - gem install mtime_cache --no-document - sudo apt-get update - sudo apt-get install -y libmysqlclient-dev diff --git a/README.md b/README.md index 1d441c47..582f4ebf 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@

[![Discord Server](https://img.shields.io/badge/discord-Sapphire-7289DA.svg)](https://discord.gg/xxcdCER) -[![Linux Build Status](https://api.travis-ci.org/SapphireServer/Sapphire.svg?branch=master)](https://travis-ci.org/SapphireMordred/Sapphire) +[![Linux Build Status](https://api.travis-ci.org/SapphireServer/Sapphire.svg?branch=master)](https://travis-ci.org/SapphireServer/Sapphire) [![Windows Build Status](https://ci.appveyor.com/api/projects/status/lil7lxa3ty165emm?svg=true)](https://ci.appveyor.com/project/SapphireMordred/Sapphire) diff --git a/bin/sql/schema/schema.sql b/bin/sql/schema/schema.sql index b87b2a9b..185d0225 100644 --- a/bin/sql/schema/schema.sql +++ b/bin/sql/schema/schema.sql @@ -530,9 +530,10 @@ CREATE TABLE `landset` ( ) ENGINE=InnoDB DEFAULT CHARSET=latin1; CREATE TABLE `houseiteminventory` ( - `landIdent` BIGINT(20) UNSIGNED NOT NULL, - `containerId` INT(10) UNSIGNED NOT NULL, - `itemId` INT(20) NOT NULL, + `LandIdent` BIGINT(20) UNSIGNED NOT NULL, + `ContainerId` INT(10) UNSIGNED NOT NULL, + `ItemId` INT(20) NOT NULL, + `SlotId` INT(10) UNSIGNED NOT NULL, INDEX `landIdent` (`landIdent`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1; diff --git a/src/common/Common.h b/src/common/Common.h index 14de2012..96e46fc8 100644 --- a/src/common/Common.h +++ b/src/common/Common.h @@ -222,10 +222,36 @@ namespace Sapphire::Common FreeCompanyGil = 22000, FreeCompanyCrystal = 22001, - HousingExternalAppearance = 25000, - HousingOutdoorItemStoreroom = 25001, - HousingInternalAppearance = 25002, - HousingIndoorItemStoreroom = 25003, + // housing interior containers + HousingInteriorAppearance = 25002, + + // 50 in each container max, 300 slots max + HousingInteriorPlacedItems1 = 25003, + HousingInteriorPlacedItems2 = 25004, + HousingInteriorPlacedItems3 = 25005, + HousingInteriorPlacedItems4 = 25006, + HousingInteriorPlacedItems5 = 25007, + HousingInteriorPlacedItems6 = 25008, + + // 50 max per container, 400 slots max + // slot limit increased 'temporarily' for relocation for all estates + // see: https://na.finalfantasyxiv.com/lodestone/topics/detail/d781e0d538428aef93b8bed4b50dd62c3c50fc74 + HousingInteriorStoreroom1 = 27001, + HousingInteriorStoreroom2 = 27002, + HousingInteriorStoreroom3 = 27003, + HousingInteriorStoreroom4 = 27004, + HousingInteriorStoreroom5 = 27005, + HousingInteriorStoreroom6 = 27006, + HousingInteriorStoreroom7 = 27007, + HousingInteriorStoreroom8 = 27008, + + + // housing exterior containers + HousingOutdoorPlacedItems = 25001, + HousingOutdoorAppearance = 25000, + HousingOutdoorStoreroom = 27000, + + }; enum ContainerType : uint16_t diff --git a/src/common/Database/ZoneDbConnection.cpp b/src/common/Database/ZoneDbConnection.cpp index 25b8e037..b35ecf79 100644 --- a/src/common/Database/ZoneDbConnection.cpp +++ b/src/common/Database/ZoneDbConnection.cpp @@ -200,6 +200,18 @@ void Sapphire::Db::ZoneDbConnection::doPrepareStatements() "UPDATE house SET BuildTime = ?, Aetheryte = ?, Comment = ?, HouseName = ?, Endorsements = ?, HousePartModels = ?, HousePartColours = ?, HouseInteriorModels = ? WHERE HouseId = ?;", CONNECTION_BOTH ); + prepareStatement( LAND_INV_SEL_ALL, + "SELECT LandIdent, ContainerId, ItemId, SlotId FROM houseiteminventory;", + CONNECTION_BOTH ); + + prepareStatement( LAND_INV_SEL_HOUSE, + "SELECT LandIdent, ContainerId, ItemId, SlotId FROM houseiteminventory WHERE LandIdent = ?", + CONNECTION_SYNC ); + + prepareStatement( LANDSET_SEL, + "SELECT * FROM land WHERE LandSetId = ?;", + 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 df8dfdf1..2cbfac6e 100644 --- a/src/common/Database/ZoneDbConnection.h +++ b/src/common/Database/ZoneDbConnection.h @@ -81,10 +81,16 @@ namespace Sapphire::Db LAND_INS, LAND_SEL, LAND_UP, + LANDSET_SEL, HOUSING_HOUSE_INS, HOUSING_HOUSE_UP, HOUSING_HOUSE_DEL, + LAND_INV_SEL_ALL, + LAND_INV_SEL_HOUSE, + LAND_INV_DEL, + LAND_INV_UP, + MAX_STATEMENTS }; diff --git a/src/common/Network/CommonActorControl.h b/src/common/Network/CommonActorControl.h index 1f55a52b..0f0eda35 100644 --- a/src/common/Network/CommonActorControl.h +++ b/src/common/Network/CommonActorControl.h @@ -304,6 +304,7 @@ enum ActorControlType : uint16_t AchievementCritReq = 0x3E8, AchievementList = 0x3E9, + SetEstateLightingLevel = 0x40B, // param1 = light level 0 - 5 maps to UI val 5-0 RequestHousingBuildPreset = 0x44C, RequestEstateHallRemoval = 0x44F, RequestBuildPreset = 0x450, // no idea what this is, it gets sent with BuildPresetHandler and has the plot id in param1 diff --git a/src/world/Actor/Player.cpp b/src/world/Actor/Player.cpp index ead5d2d4..49dfdb49 100644 --- a/src/world/Actor/Player.cpp +++ b/src/world/Actor/Player.cpp @@ -1618,7 +1618,7 @@ void Sapphire::Entity::Player::sendZonePackets() state |= HasAetheryte; } - setLandFlags( LandFlagsSlot::Private, state, pLand->getLandId(), pLand->getWardNum(), pLand->getTerritoryTypeId() ); + setLandFlags( LandFlagsSlot::Private, state, pLand->getLandIdent() ); } sendLandFlags(); @@ -1799,12 +1799,10 @@ bool Sapphire::Entity::Player::isOnEnterEventDone() const return m_onEnterEventDone; } -void Sapphire::Entity::Player::setLandFlags( uint8_t flagSlot, uint32_t landFlags, - int16_t landId, int16_t wardNum, int16_t zoneId ) +void Sapphire::Entity::Player::setLandFlags( uint8_t flagSlot, uint32_t landFlags, Common::LandIdent ident ) { - m_landFlags[ flagSlot ].landIdent.landId = landId; - m_landFlags[ flagSlot ].landIdent.wardNum = wardNum; - m_landFlags[ flagSlot ].landIdent.territoryTypeId = zoneId; + m_landFlags[ flagSlot ].landIdent = ident; + // todo: leave this in for now but we really need to handle this world id shit properly m_landFlags[ flagSlot ].landIdent.worldId = 67; m_landFlags[ flagSlot ].landFlags = landFlags; m_landFlags[ flagSlot ].unkown1 = 0; diff --git a/src/world/Actor/Player.h b/src/world/Actor/Player.h index 8532e21a..b0c8b1cf 100644 --- a/src/world/Actor/Player.h +++ b/src/world/Actor/Player.h @@ -765,7 +765,7 @@ namespace Sapphire::Entity // Housing Handling ////////////////////////////////////////////////////////////////////////////////////////////////////// - void setLandFlags( uint8_t permissionSet, uint32_t landFlags, int16_t landId, int16_t wardNum, int16_t zoneId ); + void setLandFlags( uint8_t permissionSet, uint32_t landFlags, Common::LandIdent ident ); void sendLandFlags(); void sendLandFlagsSlot( Common::LandFlagsSlot slot ); @@ -910,6 +910,8 @@ namespace Sapphire::Entity bool isObtainable( uint32_t catalogId, uint8_t quantity ); + uint32_t getNextInventorySequence(); + void send(); uint8_t getFreeSlotsInBags(); @@ -939,6 +941,8 @@ namespace Sapphire::Entity bool m_onEnterEventDone; + uint32_t m_inventorySequence; + private: using InventoryMap = std::map< uint16_t, Sapphire::ItemContainerPtr >; diff --git a/src/world/Actor/PlayerInventory.cpp b/src/world/Actor/PlayerInventory.cpp index 365bd622..67545381 100644 --- a/src/world/Actor/PlayerInventory.cpp +++ b/src/world/Actor/PlayerInventory.cpp @@ -357,14 +357,11 @@ void Sapphire::Entity::Player::removeCrystal( Common::CrystalType type, uint32_t void Sapphire::Entity::Player::sendInventory() { - InventoryMap::iterator it; - auto pInvMgr = g_fw.get< World::Manager::InventoryMgr >(); - uint32_t count = 0; - for( it = m_storageMap.begin(); it != m_storageMap.end(); ++it, ++count ) + for( auto it = m_storageMap.begin(); it != m_storageMap.end(); ++it ) { - pInvMgr->sendInventoryContainer( *this, it->second, count ); + pInvMgr->sendInventoryContainer( *this, it->second ); } } @@ -867,3 +864,8 @@ bool Sapphire::Entity::Player::collectHandInItems( std::vector< uint32_t > itemI return true; } + +uint32_t Sapphire::Entity::Player::getNextInventorySequence() +{ + return m_inventorySequence++; +} diff --git a/src/world/DebugCommand/DebugCommandHandler.cpp b/src/world/DebugCommand/DebugCommandHandler.cpp index 2eaa57ed..b3898656 100644 --- a/src/world/DebugCommand/DebugCommandHandler.cpp +++ b/src/world/DebugCommand/DebugCommandHandler.cpp @@ -1006,29 +1006,34 @@ void Sapphire::DebugCommandHandler::housing( char* data, Entity::Player& player, subCommand = params; } - if( subCommand == "permission" || subCommand == "perm" ) - { - uint8_t permissionSet; - sscanf( params.c_str(), "%hhu", &permissionSet ); - - if ( permissionSet < 5 ) - { - auto pZone = player.getCurrentZone(); - if( pTeriMgr->isHousingTerritory( pZone->getTerritoryTypeId() ) ) - { - auto pHousing = std::dynamic_pointer_cast< HousingZone >( pZone ); - if( pHousing ) - { - player.setLandFlags( permissionSet, 0, pHousing->getLandSetId(), pHousing->getWardNum(), pHousing->getTerritoryTypeId() ); - player.sendLandFlags(); - } - else - player.sendDebug( "You aren't in a housing Zone." ); - } - } - else - player.sendDebug( "PermissionSet out of range." ); - } +// if( subCommand == "permission" || subCommand == "perm" ) +// { +// uint8_t permissionSet; +// sscanf( params.c_str(), "%hhu", &permissionSet ); +// +// if ( permissionSet < 5 ) +// { +// auto pZone = player.getCurrentZone(); +// if( pTeriMgr->isHousingTerritory( pZone->getTerritoryTypeId() ) ) +// { +// auto pHousing = std::dynamic_pointer_cast< HousingZone >( pZone ); +// if( pHousing ) +// { +// // todo: wat? +// Common::LandIdent ident {}; +// ident.wardNum = pHousing->getWardNum(); +// ident.territoryTypeId = pHousing->getTerritoryTypeId(); +// +// player.setLandFlags( permissionSet, 0, pHousing->getLandSetId(), ident ); +// player.sendLandFlags(); +// } +// else +// player.sendDebug( "You aren't in a housing Zone." ); +// } +// } +// else +// player.sendDebug( "PermissionSet out of range." ); +// } else { player.sendDebug( "Unknown sub command." ); diff --git a/src/world/Manager/HousingMgr.cpp b/src/world/Manager/HousingMgr.cpp index 2a999e95..44846d0e 100644 --- a/src/world/Manager/HousingMgr.cpp +++ b/src/world/Manager/HousingMgr.cpp @@ -24,6 +24,7 @@ #include "Framework.h" #include "ServerMgr.h" #include "Territory/House.h" +#include "InventoryMgr.h" using namespace Sapphire::Common; using namespace Sapphire::Network; @@ -164,8 +165,7 @@ Sapphire::LandPurchaseResult Sapphire::World::Manager::HousingMgr::purchaseLand( pLand->setState( HouseState::sold ); pLand->setLandType( Common::LandType::Private ); - player.setLandFlags( LandFlagsSlot::Private, 0x00, plot, - pHousing->getWardNum(), pHousing->getTerritoryTypeId() ); + player.setLandFlags( LandFlagsSlot::Private, 0x00, pLand->getLandIdent() ); player.sendLandFlagsSlot( LandFlagsSlot::Private ); @@ -216,12 +216,14 @@ bool Sapphire::World::Manager::HousingMgr::relinquishLand( Entity::Player& playe pLand->setLandType( Common::LandType::none ); pLand->updateLandDb(); - player.setLandFlags( LandFlagsSlot::Private, 0x00, 0xFF, 0xFF, 0xFF ); + Common::LandIdent ident { 0xFF, 0xFF, 0xFF, 0xFF }; + + player.setLandFlags( LandFlagsSlot::Private, 0x00, ident ); player.sendLandFlagsSlot( LandFlagsSlot::Private ); auto screenMsgPkt2 = makeActorControl143( player.getId(), ActorControl::LogMsg, 3351, 0x1AA, - pLand->getWardNum() + 1, plot + 1 ); + pLand->getLandIdent().wardNum + 1, plot + 1 ); player.queuePacket( screenMsgPkt2 ); pHousing->sendLandUpdate( plot ); @@ -350,7 +352,7 @@ void Sapphire::World::Manager::HousingMgr::buildPresetEstate( Entity::Player& pl player.eventStart( player.getId(), 0x000B0095, Event::EventHandler::EventType::Housing, 1, 1 ); player.playScene( 0x000B0095, 0, SET_BASE | HIDE_HOTBAR , 0, 1, plotNum, nullptr ); - player.setLandFlags( LandFlagsSlot::Private, EstateBuilt, pLand->getLandId(), pLand->getWardNum(), pLand->getTerritoryTypeId() ); + player.setLandFlags( LandFlagsSlot::Private, EstateBuilt, pLand->getLandIdent() ); player.sendLandFlagsSlot( LandFlagsSlot::Private ); hZone->registerHouseEntranceEObj( plotNum ); @@ -508,5 +510,10 @@ void Sapphire::World::Manager::HousingMgr::sendHousingInventory( Entity::Player& if( targetLand->getOwnerId() != player.getId() ) return; - player.sendDebug( "got inventory for plot: " + targetLand->getHouse()->getHouseName() ); + auto container = targetLand->getItemContainer( inventoryType ); + if( !container ) + return; + + auto invMgr = g_fw.get< Manager::InventoryMgr >(); + invMgr->sendInventoryContainer( player, container ); } \ No newline at end of file diff --git a/src/world/Manager/InventoryMgr.cpp b/src/world/Manager/InventoryMgr.cpp index 8ce40ffe..6529d617 100644 --- a/src/world/Manager/InventoryMgr.cpp +++ b/src/world/Manager/InventoryMgr.cpp @@ -10,9 +10,9 @@ using namespace Sapphire::Network::Packets; void Sapphire::World::Manager::InventoryMgr::sendInventoryContainer( Sapphire::Entity::Player& player, - Sapphire::ItemContainerPtr container, - uint32_t sequence ) + Sapphire::ItemContainerPtr container ) { + auto sequence = player.getNextInventorySequence(); auto pMap = container->getItemMap(); for( auto itM = pMap.begin(); itM != pMap.end(); ++itM ) diff --git a/src/world/Manager/InventoryMgr.h b/src/world/Manager/InventoryMgr.h index 55869850..26fae576 100644 --- a/src/world/Manager/InventoryMgr.h +++ b/src/world/Manager/InventoryMgr.h @@ -9,8 +9,7 @@ namespace Sapphire::World::Manager class InventoryMgr { public: - void sendInventoryContainer( Sapphire::Entity::Player& player, Sapphire::ItemContainerPtr container, - uint32_t sequence = 0 ); + void sendInventoryContainer( Sapphire::Entity::Player& player, Sapphire::ItemContainerPtr container ); }; } diff --git a/src/world/Network/Handlers/ClientTriggerHandler.cpp b/src/world/Network/Handlers/ClientTriggerHandler.cpp index 40add015..de7528c2 100644 --- a/src/world/Network/Handlers/ClientTriggerHandler.cpp +++ b/src/world/Network/Handlers/ClientTriggerHandler.cpp @@ -423,16 +423,18 @@ void Sapphire::Network::GameConnection::clientTriggerHandler( const Packets::FFX } case ClientTriggerType::RequestLandInventory: { - if( param2 != 1 ) - return; - uint8_t plot = ( param12 & 0xFF ); + auto housingMgr = g_fw.get< HousingMgr >(); if( !housingMgr ) break; - housingMgr->sendHousingInventory( player, Common::InventoryType::HousingOutdoorItemStoreroom, plot ); + uint16_t inventoryType = Common::InventoryType::HousingOutdoorPlacedItems; + if( param2 == 1 ) + inventoryType = Common::InventoryType::HousingOutdoorStoreroom; + + housingMgr->sendHousingInventory( player, inventoryType, plot ); break; } @@ -446,7 +448,7 @@ void Sapphire::Network::GameConnection::clientTriggerHandler( const Packets::FFX if( !housingMgr ) break; - housingMgr->sendHousingInventory( player, Common::InventoryType::HousingIndoorItemStoreroom, 255 ); + // housingMgr->sendHousingInventory( player, Common::InventoryType::HousingInd, 255 ); break; } diff --git a/src/world/Territory/House.cpp b/src/world/Territory/House.cpp index d5b20ef2..820a7b1e 100644 --- a/src/world/Territory/House.cpp +++ b/src/world/Territory/House.cpp @@ -13,12 +13,10 @@ extern Sapphire::Framework g_fw; -Sapphire::House::House( uint32_t houseId, uint32_t landSetId, uint8_t landId, uint8_t wardNum, uint16_t territoryTypeId ) : +Sapphire::House::House( uint32_t houseId, uint32_t landSetId, Common::LandIdent ident ) : m_houseId( houseId ), m_landSetId( landSetId ), - m_landId( landId ), - m_wardNum( wardNum ), - m_territoryTypeId( territoryTypeId ) + m_landIdent( ident ) { auto pDB = g_fw.get< Db::DbWorkerPool< Db::ZoneDbConnection > >(); @@ -37,7 +35,7 @@ Sapphire::House::House( uint32_t houseId, uint32_t landSetId, uint8_t landId, ui pDB->execute( stmt ); // todo: make this nicer/configurable? - m_houseName = "Estate #" + std::to_string( landId + 1 ); + m_houseName = "Estate #" + std::to_string( m_landIdent.landId + 1 ); } else { @@ -121,19 +119,9 @@ uint32_t Sapphire::House::getLandSetId() const return m_landSetId; } -uint8_t Sapphire::House::getLandId() const +Sapphire::Common::LandIdent Sapphire::House::getLandIdent() const { - return m_landId; -} - -uint8_t Sapphire::House::getWardNum() const -{ - return m_wardNum; -} - -uint16_t Sapphire::House::getTerritoryTypeId() const -{ - return m_territoryTypeId; + return m_landIdent; } uint32_t Sapphire::House::getHouseId() const diff --git a/src/world/Territory/House.h b/src/world/Territory/House.h index 183afd1f..f228874b 100644 --- a/src/world/Territory/House.h +++ b/src/world/Territory/House.h @@ -12,7 +12,7 @@ namespace Sapphire class House { public: - House( uint32_t houseId, uint32_t landSetId, uint8_t landId, uint8_t wardNum, uint16_t territoryTypeId ); + House( uint32_t houseId, uint32_t landSetId, Common::LandIdent ident ); virtual ~House(); using HousePart = std::pair< uint32_t, uint8_t >; @@ -20,9 +20,7 @@ namespace Sapphire //gerneral uint32_t getLandSetId() const; - uint8_t getLandId() const; - uint8_t getWardNum() const; - uint16_t getTerritoryTypeId() const; + Common::LandIdent getLandIdent() const; uint32_t getHouseId() const; const std::string& getHouseName() const; @@ -45,9 +43,7 @@ namespace Sapphire private: uint32_t m_landSetId; - uint8_t m_landId; - uint8_t m_wardNum; - uint16_t m_territoryTypeId; + Common::LandIdent m_landIdent; uint32_t m_houseId; uint64_t m_buildTime; diff --git a/src/world/Territory/HousingZone.cpp b/src/world/Territory/HousingZone.cpp index 5f47d22f..17fdfd3b 100644 --- a/src/world/Territory/HousingZone.cpp +++ b/src/world/Territory/HousingZone.cpp @@ -42,12 +42,15 @@ bool Sapphire::HousingZone::init() { auto pDb = g_fw.get< Db::DbWorkerPool< Db::ZoneDbConnection > >(); - auto res = pDb->query( "SELECT * FROM landset WHERE landsetid = " + std::to_string( m_landSetId ) ); - if( !res->next() ) { - pDb->directExecute( "INSERT INTO landset ( landsetid ) VALUES ( " + std::to_string( m_landSetId ) + " );" ); + auto res = pDb->query( "SELECT * FROM landset WHERE landsetid = " + std::to_string( m_landSetId ) ); + if( !res->next() ) + { + pDb->directExecute( "INSERT INTO landset ( landsetid ) VALUES ( " + std::to_string( m_landSetId ) + " );" ); + } } + int housingIndex; if( m_territoryTypeId == 339 ) housingIndex = 0; @@ -61,25 +64,46 @@ bool Sapphire::HousingZone::init() auto pExdData = g_fw.get< Data::ExdDataGenerated >(); auto info = pExdData->get< Sapphire::Data::HousingLandSet >( housingIndex ); - uint32_t landId; - for( landId = 0; landId < 60; landId++ ) - { - auto pLand = make_Land( m_territoryTypeId, getWardNum(), landId, m_landSetId, info ); - m_landPtrMap[ landId ] = pLand; + auto stmt = pDb->getPreparedStatement( Db::LANDSET_SEL ); + stmt->setUInt64( 1, m_landSetId ); + auto res = pDb->query( stmt ); - if( auto house = pLand->getHouse() ) - { - registerHouseEntranceEObj( landId << 8 ); - } + std::vector< QueuedLandInit > landInit; + + while( res->next() ) + { + + 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 ); + } + + // nuke current query connection so the queries still in land don't fail + res.reset(); + + // spawn land + for( auto& init : landInit ) + { + 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 ); + + m_landPtrMap[ init.m_landId ] = land; + + if( init.m_houseId > 0 ) + registerHouseEntranceEObj( init.m_landId ); } return true; } -Sapphire::HousingZone::~HousingZone() -{ - -} +Sapphire::HousingZone::~HousingZone() = default; void Sapphire::HousingZone::onPlayerZoneIn( Entity::Player& player ) { diff --git a/src/world/Territory/HousingZone.h b/src/world/Territory/HousingZone.h index 3868f31a..63fcfb4b 100644 --- a/src/world/Territory/HousingZone.h +++ b/src/world/Territory/HousingZone.h @@ -53,6 +53,17 @@ 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 ee815597..d575cfea 100644 --- a/src/world/Territory/Land.cpp +++ b/src/world/Territory/Land.cpp @@ -16,6 +16,7 @@ #include "Actor/Player.h" #include "Inventory/ItemContainer.h" #include "Inventory/Item.h" +#include "Inventory/ItemUtil.h" #include "Forwards.h" #include "Land.h" @@ -28,9 +29,6 @@ using namespace Sapphire::Common; Sapphire::Land::Land( uint16_t territoryTypeId, uint8_t wardNum, uint8_t landId, uint32_t landSetId, Sapphire::Data::HousingLandSetPtr info ) : - m_territoryTypeId( territoryTypeId ), - m_wardNum( wardNum ), - m_landId( landId ), m_currentPrice( 0 ), m_minPrice( 0 ), m_nextDrop( static_cast< uint32_t >( Util::getTimeSeconds() ) + 21600 ), @@ -45,52 +43,31 @@ Sapphire::Land::Land( uint16_t territoryTypeId, uint8_t wardNum, uint8_t landId, { memset( &m_tag, 0x00, 3 ); - init(); + m_landIdent.landId = landId; + m_landIdent.territoryTypeId = territoryTypeId; + m_landIdent.wardNum = wardNum; + m_landIdent.worldId = 67; // todo: fix this + + m_minPrice = m_landInfo->minPrice[ m_landIdent.landId ]; + m_maxPrice = m_landInfo->initialPrice[ m_landIdent.landId ]; } -Sapphire::Land::~Land() +Sapphire::Land::~Land() = default; + +void Sapphire::Land::init( Common::LandType type, uint8_t size, uint8_t state, uint32_t currentPrice, uint64_t ownerId, uint64_t houseId ) { + m_type = type; + m_size = size; + m_state = state; + m_currentPrice = currentPrice; + m_ownerId = ownerId; -} - -void Sapphire::Land::init() -{ - auto pDb = g_fw.get< Db::DbWorkerPool< Db::ZoneDbConnection > >(); - auto res = pDb->query( "SELECT * FROM land WHERE LandSetId = " + std::to_string( m_landSetId ) + " " - "AND LandId = " + std::to_string( m_landId ) ); - if( !res->next() ) - { - pDb->directExecute( "INSERT INTO land ( landsetid, landid, type, size, status, landprice, UpdateTime, OwnerId, HouseId ) " - "VALUES ( " + std::to_string( m_landSetId ) + "," + std::to_string( m_landId ) + "," - + std::to_string( static_cast< uint8_t >( m_type ) ) + "," - + std::to_string( m_landInfo->plotSize[ m_landId ] ) + "," - + " 1, " + std::to_string( m_landInfo->initialPrice[ m_landId ] ) + ", 0, 0, 0 );" ); - - m_currentPrice = m_landInfo->initialPrice[ m_landId ]; - m_minPrice = m_landInfo->minPrice[ m_landId ]; - m_size = m_landInfo->plotSize[ m_landId ]; - m_state = HouseState::forSale; - } - else - { - m_type = static_cast< Common::LandType >( res->getUInt( "Type" ) ); - m_size = res->getUInt( "Size" ); - m_state = res->getUInt( "Status" ); - m_currentPrice = res->getUInt( "LandPrice" ); - m_ownerId = res->getUInt64( "OwnerId" ); - m_minPrice = m_landInfo->minPrice[ m_landId ]; - m_maxPrice = m_landInfo->initialPrice[ m_landId ]; - - auto houseId = res->getUInt( "HouseId" ); - - // fetch the house if we have one for this land - if( houseId > 0 ) - m_pHouse = make_House( houseId, m_landSetId, m_landId, m_wardNum, m_territoryTypeId ); - - } + // 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 >( getTerritoryTypeId(), getLandId() ); + auto info = pExdData->get< Sapphire::Data::HousingMapMarkerInfo >( m_landIdent.territoryTypeId, m_landIdent.landId ); if( info ) { m_mapMarkerPosition.x = info->x; @@ -117,15 +94,65 @@ void Sapphire::Land::init() } // init item containers - auto setupContainer = [ this ]( InventoryType type, uint8_t maxSize ) + auto setupContainer = [ this ]( InventoryType type, uint16_t maxSize ) { m_landInventoryMap[ type ] = make_ItemContainer( type, maxSize, "houseiteminventory", true, true ); }; - setupContainer( InventoryType::HousingExternalAppearance, 8 ); - setupContainer( InventoryType::HousingInternalAppearance, 8 ); - setupContainer( InventoryType::HousingOutdoorItemStoreroom, m_maxPlacedExternalItems ); - setupContainer( InventoryType::HousingIndoorItemStoreroom, m_maxPlacedInternalItems ); + 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 ); + } + } } uint32_t Sapphire::Land::convertItemIdToHousingItemId( uint32_t itemId ) @@ -186,19 +213,9 @@ uint32_t Sapphire::Land::getLandSetId() const return m_landSetId; } -uint8_t Sapphire::Land::getWardNum() const +Sapphire::Common::LandIdent Sapphire::Land::getLandIdent() const { - return m_wardNum; -} - -uint8_t Sapphire::Land::getLandId() const -{ - return m_landId; -} - -uint16_t Sapphire::Land::getTerritoryTypeId() const -{ - return m_territoryTypeId; + return m_landIdent; } Sapphire::HousePtr Sapphire::Land::getHouse() const @@ -286,7 +303,7 @@ void Sapphire::Land::updateLandDb() + ", HouseId = " + std::to_string( houseId ) + ", Type = " + std::to_string( static_cast< uint32_t >( m_type ) ) //TODO: add house id + " WHERE LandSetId = " + std::to_string( m_landSetId ) - + " AND LandId = " + std::to_string( m_landId ) + ";" ); + + " AND LandId = " + std::to_string( m_landIdent.landId ) + ";" ); if( auto house = getHouse() ) house->updateHouseDb(); @@ -332,7 +349,7 @@ 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(), getLandId(), getWardNum(), getTerritoryTypeId() ); + m_pHouse = make_House( newId, getLandSetId(), getLandIdent() ); } diff --git a/src/world/Territory/Land.h b/src/world/Territory/Land.h index 5aadbb94..bf8a834c 100644 --- a/src/world/Territory/Land.h +++ b/src/world/Territory/Land.h @@ -17,6 +17,7 @@ namespace Sapphire Land( uint16_t zoneId, uint8_t wardNum, uint8_t landId, uint32_t landSetId, Sapphire::Data::HousingLandSetPtr info ); virtual ~Land(); + void init( Common::LandType type, uint8_t size, uint8_t state, uint32_t currentPrice, uint64_t ownerId, uint64_t houseId ); using LandInventoryMap = std::unordered_map< uint16_t, ItemContainerPtr >; @@ -31,12 +32,10 @@ namespace Sapphire uint8_t getState() const; uint8_t getSharing() const; uint32_t getLandSetId() const; - uint8_t getWardNum() const; - uint8_t getLandId() const; - uint16_t getTerritoryTypeId() const; Common::LandType getLandType() const; Sapphire::HousePtr getHouse() const; Common::FFXIVARR_POSITION3 getMapMarkerPosition(); + Common::LandIdent getLandIdent() const; //Free Comapny void setFreeCompany( uint32_t id, uint32_t icon, uint32_t color ); @@ -62,16 +61,15 @@ namespace Sapphire uint8_t getLandTag( uint8_t slot ); ItemContainerPtr getItemContainer( uint16_t inventoryType ) const; + void loadItemContainerContents(); private: uint32_t convertItemIdToHousingItemId( uint32_t itemId ); - void init(); uint32_t getNextHouseId(); - uint8_t m_wardNum; - uint8_t m_landId; + Common::LandIdent m_landIdent; + uint32_t m_landSetId; - uint16_t m_territoryTypeId; uint8_t m_size; uint8_t m_state; Common::LandType m_type; @@ -89,8 +87,8 @@ namespace Sapphire //item storage LandInventoryMap m_landInventoryMap; - uint32_t m_maxPlacedExternalItems; - uint32_t m_maxPlacedInternalItems; + uint16_t m_maxPlacedExternalItems; + uint16_t m_maxPlacedInternalItems; //price uint32_t m_initPrice;