mirror of
https://github.com/SapphireServer/Sapphire.git
synced 2025-04-27 14:57:44 +00:00
Merge pull request #259 from GokuWeedLord/actor_rewrite
move eobj/player spawnindex generation to common manager
This commit is contained in:
commit
40fccfb481
6 changed files with 139 additions and 59 deletions
109
src/common/Util/SpawnIndexAllocator.h
Normal file
109
src/common/Util/SpawnIndexAllocator.h
Normal file
|
@ -0,0 +1,109 @@
|
|||
#ifndef SAPPHIRE_SPAWNINDEXALLOCATOR_H
|
||||
#define SAPPHIRE_SPAWNINDEXALLOCATOR_H
|
||||
|
||||
#include <queue>
|
||||
#include <unordered_map>
|
||||
#include <type_traits>
|
||||
|
||||
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
|
|
@ -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();
|
||||
|
|
|
@ -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 )
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include "Forwards.h"
|
||||
|
||||
#include <common/Common.h>
|
||||
#include <common/Util/SpawnIndexAllocator.h>
|
||||
|
||||
#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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Add table
Reference in a new issue