chore: Move Player ghosting functionality to GhostComponent (#1413)

* Moving and organizing Player code

- Move code to CharacterComponent
- Remove extraneous interfaces
- Simplify some code greatly
- Change some types to return and take in const ref (only structs larger than 8 bytes benefit from this change.)
- Update code to use CharacterComponent for sending to zone instead of Player*.

* Moving and organizing Player code

- Move code to CharacterComponent
- Remove extraneous interfaces
- Simplify some code greatly
- Change some types to return and take in const ref (only structs larger than 8 bytes benefit from this change.)
- Update code to use CharacterComponent for sending to zone instead of Player*.
- Remove static storage container (static containers can be destroyed before exit/terminate handler executes)

* remove player cast

* Remove extra includes

* Add a player manager

Used for the static Player functions.  Further removes stuff from the Player class/file.

* chore: Move ghosting functionality to component

Tested that ghosting still works and players are still firing off the OnPlayerLeave and relevant handlers.

* move to unordered_set
This commit is contained in:
David Markowitz 2024-01-14 11:10:13 -08:00 committed by GitHub
parent 0a30430c4f
commit c83ec8228c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 138 additions and 144 deletions

View File

@ -2135,9 +2135,9 @@ void Entity::ProcessPositionUpdate(PositionUpdate& update) {
controllablePhysicsComponent->SetAngularVelocity(update.angularVelocity);
controllablePhysicsComponent->SetDirtyAngularVelocity(update.angularVelocity != NiPoint3::ZERO);
auto* player = static_cast<Player*>(this);
player->SetGhostReferencePoint(update.position);
Game::entityManager->QueueGhostUpdate(player->GetObjectID());
auto* ghostComponent = GetComponent<GhostComponent>();
if (ghostComponent) ghostComponent->SetGhostReferencePoint(update.position);
Game::entityManager->QueueGhostUpdate(GetObjectID());
if (updateChar) Game::entityManager->SerializeEntity(this);
}

View File

@ -24,6 +24,7 @@
#include "eReplicaComponentType.h"
#include "eReplicaPacketType.h"
#include "PlayerManager.h"
#include "GhostComponent.h"
// Configure which zones have ghosting disabled, mostly small worlds.
std::vector<LWOMAPID> EntityManager::m_GhostingExcludedZones = {
@ -189,7 +190,8 @@ void EntityManager::SerializeEntities() {
if (entity->GetIsGhostingCandidate()) {
for (auto* player : PlayerManager::GetAllPlayers()) {
if (player->IsObserved(toSerialize)) {
auto* ghostComponent = player->GetComponent<GhostComponent>();
if (ghostComponent && ghostComponent->IsObserved(toSerialize)) {
Game::server->Send(&stream, player->GetSystemAddress(), false);
}
}
@ -381,7 +383,8 @@ void EntityManager::ConstructEntity(Entity* entity, const SystemAddress& sysAddr
if (player->GetPlayerReadyForUpdates()) {
Game::server->Send(&stream, player->GetSystemAddress(), false);
} else {
player->AddLimboConstruction(entity->GetObjectID());
auto* ghostComponent = player->GetComponent<GhostComponent>();
if (ghostComponent) ghostComponent->AddLimboConstruction(entity->GetObjectID());
}
}
}
@ -421,7 +424,8 @@ void EntityManager::DestructEntity(Entity* entity, const SystemAddress& sysAddr)
for (auto* player : PlayerManager::GetAllPlayers()) {
if (!player->GetPlayerReadyForUpdates()) {
player->RemoveLimboConstruction(entity->GetObjectID());
auto* ghostComponent = player->GetComponent<GhostComponent>();
if (ghostComponent) ghostComponent->RemoveLimboConstruction(entity->GetObjectID());
}
}
}
@ -484,13 +488,14 @@ void EntityManager::UpdateGhosting(Player* player) {
}
auto* missionComponent = player->GetComponent<MissionComponent>();
auto* ghostComponent = player->GetComponent<GhostComponent>();
if (missionComponent == nullptr) {
if (missionComponent == nullptr || !ghostComponent) {
return;
}
const auto& referencePoint = player->GetGhostReferencePoint();
const auto isOverride = player->GetGhostOverride();
const auto& referencePoint = ghostComponent->GetGhostReferencePoint();
const auto isOverride = ghostComponent->GetGhostOverride();
for (auto* entity : m_EntitiesToGhost) {
const auto isAudioEmitter = entity->GetLOT() == 6368;
@ -499,7 +504,7 @@ void EntityManager::UpdateGhosting(Player* player) {
const int32_t id = entity->GetObjectID();
const auto observed = player->IsObserved(id);
const auto observed = ghostComponent->IsObserved(id);
const auto distance = NiPoint3::DistanceSquared(referencePoint, entityPoint);
@ -511,7 +516,7 @@ void EntityManager::UpdateGhosting(Player* player) {
}
if (observed && distance > ghostingDistanceMax && !isOverride) {
player->GhostEntity(id);
ghostComponent->GhostEntity(id);
DestructEntity(entity, player->GetSystemAddress());
@ -528,7 +533,7 @@ void EntityManager::UpdateGhosting(Player* player) {
}
}
player->ObserveEntity(id);
ghostComponent->ObserveEntity(id);
ConstructEntity(entity, player->GetSystemAddress());
@ -550,22 +555,25 @@ void EntityManager::CheckGhosting(Entity* entity) {
const auto isAudioEmitter = entity->GetLOT() == 6368;
for (auto* player : PlayerManager::GetAllPlayers()) {
const auto& entityPoint = player->GetGhostReferencePoint();
auto* ghostComponent = player->GetComponent<GhostComponent>();
if (!ghostComponent) continue;
const auto& entityPoint = ghostComponent->GetGhostReferencePoint();
const int32_t id = entity->GetObjectID();
const auto observed = player->IsObserved(id);
const auto observed = ghostComponent->IsObserved(id);
const auto distance = NiPoint3::DistanceSquared(referencePoint, entityPoint);
if (observed && distance > ghostingDistanceMax) {
player->GhostEntity(id);
ghostComponent->GhostEntity(id);
DestructEntity(entity, player->GetSystemAddress());
entity->SetObservers(entity->GetObservers() - 1);
} else if (!observed && ghostingDistanceMin > distance) {
player->ObserveEntity(id);
ghostComponent->ObserveEntity(id);
ConstructEntity(entity, player->GetSystemAddress());

View File

@ -14,14 +14,6 @@
#include "eReplicaComponentType.h"
#include "PlayerManager.h"
void Player::SetGhostReferencePoint(const NiPoint3& value) {
m_GhostReferencePoint = value;
}
void Player::SetGhostOverridePoint(const NiPoint3& value) {
m_GhostOverridePoint = value;
}
void Player::SetRespawnPos(const NiPoint3& position) {
if (!m_Character) return;
@ -47,85 +39,16 @@ Player::Player(const LWOOBJID& objectID, const EntityInfo info, User* user, Enti
m_SystemAddress = m_ParentUser->GetSystemAddress();
m_DroppedCoins = 0;
m_GhostReferencePoint = NiPoint3::ZERO;
m_GhostOverridePoint = NiPoint3::ZERO;
m_GhostOverride = false;
int32_t initialObservedEntitiesCapacity = 256;
m_ObservedEntities.resize(initialObservedEntitiesCapacity);
m_Character->SetEntity(this);
PlayerManager::AddPlayer(this);
}
void Player::AddLimboConstruction(LWOOBJID objectId) {
const auto iter = std::find(m_LimboConstructions.begin(), m_LimboConstructions.end(), objectId);
if (iter == m_LimboConstructions.end()) {
m_LimboConstructions.push_back(objectId);
}
}
void Player::RemoveLimboConstruction(LWOOBJID objectId) {
const auto iter = std::find(m_LimboConstructions.begin(), m_LimboConstructions.end(), objectId);
if (iter != m_LimboConstructions.end()) {
m_LimboConstructions.erase(iter);
}
}
void Player::ConstructLimboEntities() {
for (const auto& objectId : m_LimboConstructions) {
auto* entity = Game::entityManager->GetEntity(objectId);
if (!entity) continue;
Game::entityManager->ConstructEntity(entity, m_SystemAddress);
}
m_LimboConstructions.clear();
}
void Player::ObserveEntity(int32_t id) {
for (auto& observedEntity : m_ObservedEntities) {
if (observedEntity == 0 || observedEntity == id) {
observedEntity = id;
return;
}
}
m_ObservedEntities.reserve(m_ObservedEntities.size() + 1);
m_ObservedEntities.push_back(id);
}
bool Player::IsObserved(int32_t id) {
return std::find(m_ObservedEntities.begin(), m_ObservedEntities.end(), id) != m_ObservedEntities.end();
}
void Player::GhostEntity(int32_t id) {
for (auto& observedEntity : m_ObservedEntities) {
if (observedEntity == id) {
observedEntity = 0;
}
}
}
Player::~Player() {
LOG("Deleted player");
for (auto& observedEntity : m_ObservedEntities) {
if (observedEntity == 0) continue;
auto* entity = Game::entityManager->GetGhostCandidate(observedEntity);
if (!entity) continue;
entity->SetObservers(entity->GetObservers() - 1);
}
m_LimboConstructions.clear();
// Make sure the player exists first. Remove afterwards to prevent the OnPlayerExist functions from not being able to find the player.
if (!PlayerManager::GetPlayer(GetObjectID())) {
if (!PlayerManager::RemovePlayer(this)) {
LOG("Unable to find player to remove from manager.");
return;
}
@ -145,6 +68,4 @@ Player::~Player() {
}
}
}
PlayerManager::RemovePlayer(this);
}

View File

@ -26,14 +26,6 @@ public:
const NiQuaternion& GetRespawnRotation() const override { return m_respawnRot; };
const NiPoint3& GetGhostReferencePoint() const { return m_GhostOverride ? m_GhostOverridePoint : m_GhostReferencePoint; };
const NiPoint3& GetOriginGhostReferencePoint() const { return m_GhostReferencePoint; };
const NiPoint3& GetGhostOverridePoint() const { return m_GhostOverridePoint; };
bool GetGhostOverride() const { return m_GhostOverride; };
std::map<LWOOBJID, Loot::Info>& GetDroppedLoot() { return m_DroppedLoot; };
uint64_t GetDroppedCoins() const { return m_DroppedCoins; };
@ -42,8 +34,6 @@ public:
* Setters
*/
void SetGhostOverride(bool value) { m_GhostOverride = value; };
void SetDroppedCoins(const uint64_t value) { m_DroppedCoins = value; };
void SetSystemAddress(const SystemAddress& value) override;
@ -52,26 +42,10 @@ public:
void SetRespawnRot(const NiQuaternion& rotation) override;
void SetGhostReferencePoint(const NiPoint3& value);
void SetGhostOverridePoint(const NiPoint3& value);
/**
* Ghosting
*/
void AddLimboConstruction(LWOOBJID objectId);
void RemoveLimboConstruction(LWOOBJID objectId);
void ConstructLimboEntities();
void ObserveEntity(const int32_t id);
bool IsObserved(const int32_t id);
void GhostEntity(const int32_t id);
~Player() override;
private:
SystemAddress m_SystemAddress;
@ -82,16 +56,6 @@ private:
User* m_ParentUser;
NiPoint3 m_GhostReferencePoint;
NiPoint3 m_GhostOverridePoint;
bool m_GhostOverride;
std::vector<int32_t> m_ObservedEntities;
std::vector<LWOOBJID> m_LimboConstructions;
std::map<LWOOBJID, Loot::Info> m_DroppedLoot;
uint64_t m_DroppedCoins;

View File

@ -22,12 +22,15 @@ void PlayerManager::AddPlayer(Player* player) {
}
}
void PlayerManager::RemovePlayer(Player* player) {
bool PlayerManager::RemovePlayer(Player* player) {
const auto iter = std::find(m_Players.begin(), m_Players.end(), player);
if (iter != m_Players.end()) {
const bool toReturn = iter != m_Players.end();
if (toReturn) {
m_Players.erase(iter);
}
return toReturn;
}
Player* PlayerManager::GetPlayer(const SystemAddress& sysAddr) {

View File

@ -11,7 +11,7 @@ struct SystemAddress;
namespace PlayerManager {
void AddPlayer(Player* player);
void RemovePlayer(Player* player);
bool RemovePlayer(Player* player);
Player* GetPlayer(const SystemAddress& sysAddr);

View File

@ -1,4 +1,57 @@
#include "GhostComponent.h"
// TODO Move ghosting related code from Player to here
GhostComponent::GhostComponent(Entity* parent) : Component(parent) {}
GhostComponent::GhostComponent(Entity* parent) : Component(parent) {
m_GhostReferencePoint = NiPoint3::ZERO;
m_GhostOverridePoint = NiPoint3::ZERO;
m_GhostOverride = false;
}
GhostComponent::~GhostComponent() {
for (auto& observedEntity : m_ObservedEntities) {
if (observedEntity == 0) continue;
auto* entity = Game::entityManager->GetGhostCandidate(observedEntity);
if (!entity) continue;
entity->SetObservers(entity->GetObservers() - 1);
}
}
void GhostComponent::SetGhostReferencePoint(const NiPoint3& value) {
m_GhostReferencePoint = value;
}
void GhostComponent::SetGhostOverridePoint(const NiPoint3& value) {
m_GhostOverridePoint = value;
}
void GhostComponent::AddLimboConstruction(LWOOBJID objectId) {
m_LimboConstructions.insert(objectId);
}
void GhostComponent::RemoveLimboConstruction(LWOOBJID objectId) {
m_LimboConstructions.erase(objectId);
}
void GhostComponent::ConstructLimboEntities() {
for (const auto& objectId : m_LimboConstructions) {
auto* entity = Game::entityManager->GetEntity(objectId);
if (!entity) continue;
Game::entityManager->ConstructEntity(entity, m_Parent->GetSystemAddress());
}
m_LimboConstructions.clear();
}
void GhostComponent::ObserveEntity(int32_t id) {
m_ObservedEntities.insert(id);
}
bool GhostComponent::IsObserved(int32_t id) {
return m_ObservedEntities.contains(id);
}
void GhostComponent::GhostEntity(int32_t id) {
m_ObservedEntities.erase(id);
}

View File

@ -3,11 +3,52 @@
#include "Component.h"
#include "eReplicaComponentType.h"
#include <unordered_set>
class NiPoint3;
class GhostComponent : public Component {
public:
static inline const eReplicaComponentType ComponentType = eReplicaComponentType::GHOST;
GhostComponent(Entity* parent);
~GhostComponent() override;
void SetGhostOverride(bool value) { m_GhostOverride = value; };
const NiPoint3& GetGhostReferencePoint() const { return m_GhostOverride ? m_GhostOverridePoint : m_GhostReferencePoint; };
const NiPoint3& GetOriginGhostReferencePoint() const { return m_GhostReferencePoint; };
const NiPoint3& GetGhostOverridePoint() const { return m_GhostOverridePoint; };
bool GetGhostOverride() const { return m_GhostOverride; };
void SetGhostReferencePoint(const NiPoint3& value);
void SetGhostOverridePoint(const NiPoint3& value);
void AddLimboConstruction(const LWOOBJID objectId);
void RemoveLimboConstruction(const LWOOBJID objectId);
void ConstructLimboEntities();
void ObserveEntity(const int32_t id);
bool IsObserved(const int32_t id);
void GhostEntity(const int32_t id);
private:
NiPoint3 m_GhostReferencePoint;
NiPoint3 m_GhostOverridePoint;
std::unordered_set<int32_t> m_ObservedEntities;
std::unordered_set<LWOOBJID> m_LimboConstructions;
bool m_GhostOverride;
};
#endif //!__GHOSTCOMPONENT__H__

View File

@ -37,6 +37,7 @@
#include "eGameMessageType.h"
#include "ePlayerFlag.h"
#include "dConfig.h"
#include "GhostComponent.h"
#include "StringifiedEnum.h"
void GameMessageHandler::HandleMessage(RakNet::BitStream* inStream, const SystemAddress& sysAddr, LWOOBJID objectID, eGameMessageType messageID) {
@ -108,9 +109,9 @@ void GameMessageHandler::HandleMessage(RakNet::BitStream* inStream, const System
GameMessages::SendRestoreToPostLoadStats(entity, sysAddr);
entity->SetPlayerReadyForUpdates();
auto* player = dynamic_cast<Player*>(entity);
if (player != nullptr) {
player->ConstructLimboEntities();
auto* ghostComponent = entity->GetComponent<GhostComponent>();
if (ghostComponent != nullptr) {
ghostComponent->ConstructLimboEntities();
}
InventoryComponent* inv = entity->GetComponent<InventoryComponent>();
@ -137,14 +138,14 @@ void GameMessageHandler::HandleMessage(RakNet::BitStream* inStream, const System
Entity* zoneControl = Game::entityManager->GetZoneControlEntity();
for (CppScripts::Script* script : CppScripts::GetEntityScripts(zoneControl)) {
script->OnPlayerLoaded(zoneControl, player);
script->OnPlayerLoaded(zoneControl, entity);
}
std::vector<Entity*> scriptedActs = Game::entityManager->GetEntitiesByComponent(eReplicaComponentType::SCRIPT);
for (Entity* scriptEntity : scriptedActs) {
if (scriptEntity->GetObjectID() != zoneControl->GetObjectID()) { // Don't want to trigger twice on instance worlds
for (CppScripts::Script* script : CppScripts::GetEntityScripts(scriptEntity)) {
script->OnPlayerLoaded(scriptEntity, player);
script->OnPlayerLoaded(scriptEntity, entity);
}
}
}

View File

@ -78,6 +78,7 @@
#include "RailActivatorComponent.h"
#include "LevelProgressionComponent.h"
#include "DonationVendorComponent.h"
#include "GhostComponent.h"
// Message includes:
#include "dZoneManager.h"
@ -4605,7 +4606,8 @@ void GameMessages::HandleToggleGhostReferenceOverride(RakNet::BitStream* inStrea
auto* player = PlayerManager::GetPlayer(sysAddr);
if (player != nullptr) {
player->SetGhostOverride(bOverride);
auto* ghostComponent = entity->GetComponent<GhostComponent>();
if (ghostComponent) ghostComponent->SetGhostOverride(bOverride);
Game::entityManager->UpdateGhosting(player);
}
@ -4620,7 +4622,8 @@ void GameMessages::HandleSetGhostReferencePosition(RakNet::BitStream* inStream,
auto* player = PlayerManager::GetPlayer(sysAddr);
if (player != nullptr) {
player->SetGhostOverridePoint(position);
auto* ghostComponent = entity->GetComponent<GhostComponent>();
if (ghostComponent) ghostComponent->SetGhostOverridePoint(position);
Game::entityManager->UpdateGhosting(player);
}