From 810d71f262afec94355f6375a455c0f7ec9d1027 Mon Sep 17 00:00:00 2001 From: Adam Date: Fri, 2 Mar 2018 02:55:02 +1100 Subject: [PATCH] move eobj/player spawnindex generation to common manager --- src/common/Util/SpawnIndexAllocator.h | 109 ++++++++++++++++++ .../sapphire_zone/Actor/EventObject.cpp | 6 +- src/servers/sapphire_zone/Actor/Player.cpp | 60 +++------- src/servers/sapphire_zone/Actor/Player.h | 17 ++- src/servers/sapphire_zone/Actor/PlayerSql.cpp | 3 - .../PacketWrappers/PlayerSpawnPacket.h | 3 + 6 files changed, 139 insertions(+), 59 deletions(-) create mode 100644 src/common/Util/SpawnIndexAllocator.h diff --git a/src/common/Util/SpawnIndexAllocator.h b/src/common/Util/SpawnIndexAllocator.h new file mode 100644 index 00000000..85875a67 --- /dev/null +++ b/src/common/Util/SpawnIndexAllocator.h @@ -0,0 +1,109 @@ +#ifndef SAPPHIRE_SPAWNINDEXALLOCATOR_H +#define SAPPHIRE_SPAWNINDEXALLOCATOR_H + +#include +#include +#include + +namespace Core +{ +namespace Util +{ + template< typename T, typename ActorIdType = uint32_t > + class SpawnIndexAllocator + { + public: + static_assert( std::is_same< T, uint8_t >::value || std::is_same< T, uint16_t >::value || + std::is_same< T, uint32_t >::value || std::is_same< T, uint64_t >::value, + "T must be uint8_t, uint16_t, uint32_t, uint64_t" ); + + SpawnIndexAllocator() : + m_maxSlotId( 0 ), + m_reserveFirstSlot( false ) + { } + + void init( T maxSlotId, bool reserveFirstSlot = false ) + { + setupQueue(); + + m_maxSlotId = maxSlotId; + m_reserveFirstSlot = reserveFirstSlot; + + // todo: reserve max slot id in map to prevent any runtime reshashing + } + + T freeUsedSpawnIndex( ActorIdType actorId ) + { + assert( m_maxSlotId != 0 ); + + auto it = m_actorIdToAllocatedMap.find( actorId ); + if( it == m_actorIdToAllocatedMap.end() ) + return 0; + + auto index = it->second; + m_availableIds.push( index ); + m_actorIdToAllocatedMap.erase( it ); + + return index; + } + + bool getNextFreeSpawnIndex( ActorIdType actorId ) + { + assert( m_maxSlotId != 0 ); + + if( m_availableIds.empty() ) + return getAllocFailId(); + + auto nextId = m_availableIds.front(); + m_availableIds.pop(); + + m_actorIdToAllocatedMap[actorId] = nextId; + + return nextId; + } + + void freeAllSpawnIndexes() + { + setupQueue(); + + m_actorIdToAllocatedMap.clear(); + } + + bool isSpawnIndexValid( T spawnIndex ) + { + return spawnIndex != getAllocFailId(); + } + + constexpr T getAllocFailId() + { + return static_cast< T >( -1 ); + } + + protected: + void setupQueue() + { + while( !m_availableIds.empty() ) + m_availableIds.pop(); + + uint32_t start = 0; + + // slot 0 is reserved when used for spawning actors/players otherwise the local player actor spawnIndex + // will be used by another actor and despawn the local player + if( m_reserveFirstSlot ) + start = 1; + + for( uint32_t i = start; i < m_maxSlotId; i++ ) + m_availableIds.push( i ); + } + + std::queue< T > m_availableIds; + std::unordered_map< ActorIdType, T > m_actorIdToAllocatedMap; + + T m_maxSlotId; + bool m_reserveFirstSlot; + }; +} +} + + +#endif //SAPPHIRE_SPAWNINDEXALLOCATOR_H diff --git a/src/servers/sapphire_zone/Actor/EventObject.cpp b/src/servers/sapphire_zone/Actor/EventObject.cpp index 4592fad5..ef4d784a 100644 --- a/src/servers/sapphire_zone/Actor/EventObject.cpp +++ b/src/servers/sapphire_zone/Actor/EventObject.cpp @@ -98,9 +98,13 @@ Core::InstanceContentPtr Core::Entity::EventObject::getParentInstance() const void Core::Entity::EventObject::spawn( Core::Entity::PlayerPtr pTarget ) { + auto spawnIndex = pTarget->getNextObjSpawnIndexForActorId( getId( ) ); + if( !pTarget->isObjSpawnIndexValid( spawnIndex ) ) + return; + g_log.debug( "Spawning EObj: id:" + std::to_string( getId() ) + " name:" + getName() ); ZoneChannelPacket< FFXIVIpcObjectSpawn > eobjStatePacket( getId(), pTarget->getId() ); - eobjStatePacket.data().spawnIndex = pTarget->getNextObjSpawnIndexForActorId( getId( )); + eobjStatePacket.data().spawnIndex = spawnIndex; eobjStatePacket.data().objKind = getObjKind(); eobjStatePacket.data().state = getState(); eobjStatePacket.data().objId = getObjectId(); diff --git a/src/servers/sapphire_zone/Actor/Player.cpp b/src/servers/sapphire_zone/Actor/Player.cpp index a5afe5b3..0e2b299a 100644 --- a/src/servers/sapphire_zone/Actor/Player.cpp +++ b/src/servers/sapphire_zone/Actor/Player.cpp @@ -73,7 +73,6 @@ Core::Entity::Player::Player() : m_markedForRemoval( false ), m_mount( 0 ), m_directorInitialized( false ), - m_objCount( 0 ), m_onEnterEventDone( false ) { m_id = 0; @@ -90,6 +89,9 @@ Core::Entity::Player::Player() : memset( m_searchMessage, 0, sizeof( m_searchMessage ) ); memset( m_classArray, 0, sizeof( m_classArray ) ); memset( m_expArray, 0, sizeof( m_expArray ) ); + + m_objSpawnIndexAllocator.init( MAX_DISPLAYED_EOBJS ); + m_actorSpawnIndexAllocator.init( MAX_DISPLAYED_ACTORS, true ); } Core::Entity::Player::~Player() @@ -438,43 +440,27 @@ uint8_t Core::Entity::Player::getGender() const void Core::Entity::Player::initSpawnIdQueue() { - while( !m_freeSpawnIdQueue.empty() ) - { - m_freeSpawnIdQueue.pop(); - } - - for( int32_t i = 1; i < MAX_DISPLAYED_ACTORS; i++ ) - { - m_freeSpawnIdQueue.push( i ); - } + m_actorSpawnIndexAllocator.freeAllSpawnIndexes(); } uint8_t Core::Entity::Player::getSpawnIdForActorId( uint32_t actorId ) { - if( m_freeSpawnIdQueue.empty() ) - return 0; - - uint8_t spawnId = m_freeSpawnIdQueue.front(); - m_freeSpawnIdQueue.pop(); - m_playerIdToSpawnIdMap[actorId] = spawnId; - return spawnId; + return m_actorSpawnIndexAllocator.getNextFreeSpawnIndex( actorId ); } -void Core::Entity::Player::assignSpawnIdToPlayerId( uint32_t actorId, uint8_t spawnId ) +bool Core::Entity::Player::isActorSpawnIdValid( uint8_t spawnIndex ) { - m_playerIdToSpawnIdMap[actorId] = spawnId; + return m_actorSpawnIndexAllocator.isSpawnIndexValid( spawnIndex ); } void Core::Entity::Player::registerAetheryte( uint8_t aetheryteId ) { - uint16_t index; uint8_t value; Util::valueToFlagByteIndexValue( aetheryteId, value, index ); m_aetheryte[index] |= value; queuePacket( ActorControlPacket143( getId(), LearnTeleport, aetheryteId, 1 ) ); - } bool Core::Entity::Player::isAetheryteRegistered( uint8_t aetheryteId ) const @@ -1048,9 +1034,7 @@ void Core::Entity::Player::onMobKill( uint16_t nameId ) void Core::Entity::Player::freePlayerSpawnId( uint32_t actorId ) { - uint8_t spawnId = m_playerIdToSpawnIdMap[actorId]; - m_playerIdToSpawnIdMap.erase( actorId ); - m_freeSpawnIdQueue.push( spawnId ); + auto spawnId = m_actorSpawnIndexAllocator.freeUsedSpawnIndex( actorId ); ZoneChannelPacket< FFXIVIpcActorFreeSpawn > freeActorSpawnPacket( getId() ); freeActorSpawnPacket.data().actorId = actorId; @@ -1661,36 +1645,22 @@ void Core::Entity::Player::teleportQuery( uint16_t aetheryteId ) uint8_t Core::Entity::Player::getNextObjSpawnIndexForActorId( uint32_t actorId ) { - // todo: fix it so the case where there's no ids available doesn't break everything :( - auto nextCount = m_freeObjCounts.front(); - m_freeObjCounts.pop(); - - m_actorIdToObjCountMap[actorId] = nextCount; - - return nextCount; + return m_objSpawnIndexAllocator.getNextFreeSpawnIndex( actorId ); } void Core::Entity::Player::resetObjSpawnIndex() { - while( m_freeObjCounts.empty() ) - m_freeObjCounts.pop(); - - for( uint32_t i = 0; i < MAX_DISPLAYED_EOBJS; ++i ) - m_freeObjCounts.push( i ); - - m_actorIdToObjCountMap.clear(); + m_objSpawnIndexAllocator.freeAllSpawnIndexes(); } void Core::Entity::Player::freeObjSpawnIndexForActorId( uint32_t actorId ) { - auto it = m_actorIdToObjCountMap.find( actorId ); - if( it == m_actorIdToObjCountMap.end() ) - return; + m_objSpawnIndexAllocator.freeUsedSpawnIndex( actorId ); +} - auto freeCount = m_actorIdToObjCountMap[actorId]; - m_freeObjCounts.push( freeCount ); - - m_actorIdToObjCountMap.erase( actorId ); +bool Core::Entity::Player::isObjSpawnIndexValid( uint8_t index ) +{ + return m_objSpawnIndexAllocator.isSpawnIndexValid( index ); } void Core::Entity::Player::setOnEnterEventDone( bool isDone ) diff --git a/src/servers/sapphire_zone/Actor/Player.h b/src/servers/sapphire_zone/Actor/Player.h index 877de137..89d4ff69 100644 --- a/src/servers/sapphire_zone/Actor/Player.h +++ b/src/servers/sapphire_zone/Actor/Player.h @@ -4,6 +4,7 @@ #include "Forwards.h" #include +#include #include "Chara.h" #include "Inventory/Inventory.h" @@ -419,10 +420,10 @@ public: void initSpawnIdQueue(); /*! get the spawn id mapped to a specific actorId */ uint8_t getSpawnIdForActorId( uint32_t actorId ); - /*! assigns the given spawnId to the actor */ - void assignSpawnIdToPlayerId( uint32_t actorId, uint8_t spawnId ); /*! frees the spawnId assigned to the given actor */ void freePlayerSpawnId( uint32_t actorId ); + /*! checks if the given spawn id is valid */ + bool isActorSpawnIdValid( uint8_t spawnId ); /*! send spawn packets to pTarget */ void spawn( PlayerPtr pTarget ) override; /*! send despawn packets to pTarget */ @@ -440,8 +441,6 @@ public: bool hasStateFlag( Common::PlayerStateFlag flag ) const; /* reset a specified flag */ void unsetStateFlag( Common::PlayerStateFlag flag ); - /* helper function, send an empty state flag update */ - void unlock(); // Player Session Handling ////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -568,6 +567,8 @@ public: void resetObjSpawnIndex(); /*! frees an obj count to be used by another eobj */ void freeObjSpawnIndexForActorId( uint32_t actorId ); + /*! checks if a spawn index is valid */ + bool isObjSpawnIndexValid( uint8_t index ); private: uint32_t m_lastWrite; @@ -641,8 +642,6 @@ private: std::map< uint32_t, Event::EventHandlerPtr > m_eventHandlerMap; - std::map< uint32_t, uint8_t > m_playerIdToSpawnIdMap; // maps player to spawn id - std::queue< uint8_t > m_freeSpawnIdQueue; // queue with spawn ids free to be assigned std::queue< uint8_t > m_freeHateSlotQueue; // queue with "hate slots" free to be assigned std::map< uint32_t, uint8_t > m_actorIdTohateSlotMap; @@ -682,10 +681,8 @@ private: uint8_t m_mount; - // counter used to index objects spawned for the player - uint8_t m_objCount; - std::queue< uint8_t > m_freeObjCounts; - std::map< uint32_t, uint8_t > m_actorIdToObjCountMap; + Util::SpawnIndexAllocator< uint8_t > m_objSpawnIndexAllocator; + Util::SpawnIndexAllocator< uint8_t > m_actorSpawnIndexAllocator; }; } diff --git a/src/servers/sapphire_zone/Actor/PlayerSql.cpp b/src/servers/sapphire_zone/Actor/PlayerSql.cpp index ab127e0f..484bb27b 100644 --- a/src/servers/sapphire_zone/Actor/PlayerSql.cpp +++ b/src/servers/sapphire_zone/Actor/PlayerSql.cpp @@ -236,9 +236,6 @@ bool Core::Entity::Player::load( uint32_t charId, SessionPtr pSession ) initSpawnIdQueue(); - if( !m_playerIdToSpawnIdMap.empty() ) - m_playerIdToSpawnIdMap.clear(); - if( !g_territoryMgr.movePlayer( pCurrZone, getAsPlayer() ) ) return false; diff --git a/src/servers/sapphire_zone/Network/PacketWrappers/PlayerSpawnPacket.h b/src/servers/sapphire_zone/Network/PacketWrappers/PlayerSpawnPacket.h index c56f9ccf..06ecc139 100644 --- a/src/servers/sapphire_zone/Network/PacketWrappers/PlayerSpawnPacket.h +++ b/src/servers/sapphire_zone/Network/PacketWrappers/PlayerSpawnPacket.h @@ -84,6 +84,9 @@ namespace Server { else { m_data.spawnIndex = target.getSpawnIdForActorId( player.getId() ); + + if( !target.isActorSpawnIdValid( m_data.spawnIndex ) ) + return; } // 0x20 == spawn hidden to be displayed by the spawneffect control m_data.displayFlags = player.getStance();