From 4b60d55d8d97da113dbb2e90c2a4834e8e813629 Mon Sep 17 00:00:00 2001 From: Tiger Wang Date: Mon, 7 Feb 2022 00:53:44 +0000 Subject: Spectation: keep track of spectators + Keep a list of spectators so that pointer clean-up can happen when the spectated is killed. * Fix invalid game state when riding/spectating and then entering or leaving spectator mode. --- src/Entities/Entity.cpp | 29 +++++++++++++++++++++++++++++ src/Entities/Entity.h | 9 +++++++++ src/Entities/Player.cpp | 39 +++++++++++++++++++++++++++++---------- src/Entities/Player.h | 7 +++++-- 4 files changed, 72 insertions(+), 12 deletions(-) diff --git a/src/Entities/Entity.cpp b/src/Entities/Entity.cpp index 0bb8b97bc..b6a67859b 100644 --- a/src/Entities/Entity.cpp +++ b/src/Entities/Entity.cpp @@ -134,6 +134,15 @@ bool cEntity::Initialize(OwnedEntity a_Self, cWorld & a_EntityWorld) +void cEntity::OnAcquireSpectator(cPlayer & a_Player) +{ + m_Spectators.push_back(&a_Player); +} + + + + + void cEntity::OnAddToWorld(cWorld & a_World) { // Spawn the entity on the clients: @@ -146,6 +155,19 @@ void cEntity::OnAddToWorld(cWorld & a_World) +void cEntity::OnLoseSpectator(cPlayer & a_Player) +{ + const auto Spectator = std::find(m_Spectators.begin(), m_Spectators.end(), &a_Player); + + ASSERT(Spectator != m_Spectators.end()); + std::swap(*Spectator, m_Spectators.back()); + m_Spectators.pop_back(); +} + + + + + void cEntity::OnRemoveFromWorld(cWorld & a_World) { // Remove all mobs from the leashed list of mobs: @@ -154,6 +176,13 @@ void cEntity::OnRemoveFromWorld(cWorld & a_World) m_LeashedMobs.front()->Unleash(false, true); } + for (const auto Player : m_Spectators) + { + Player->OnLoseSpectated(); + } + + m_Spectators.clear(); + if (m_AttachedTo != nullptr) { Detach(); diff --git a/src/Entities/Entity.h b/src/Entities/Entity.h index 941d07339..2f9ba229b 100644 --- a/src/Entities/Entity.h +++ b/src/Entities/Entity.h @@ -135,11 +135,17 @@ public: Adds the entity to the world. */ bool Initialize(OwnedEntity a_Self, cWorld & a_EntityWorld); + /** Called when a player begins spectating this entity. */ + void OnAcquireSpectator(cPlayer & a_Player); + /** Called when the entity is added to a world. e.g after first spawning or after successfuly moving between worlds. \param a_World The world being added to. */ virtual void OnAddToWorld(cWorld & a_World); + /** Called when a player stops spectating this entity. */ + void OnLoseSpectator(cPlayer & a_Player); + /** Called when the entity is removed from a world. e.g. When the entity is destroyed or moved to a different world. \param a_World The world being removed from. */ @@ -723,4 +729,7 @@ private: /** List of leashed mobs to this entity */ cMonsterList m_LeashedMobs; + + /** List of players who are spectating this entity. */ + std::vector m_Spectators; } ; // tolua_export diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp index 30650a6d0..3fd2aac48 100644 --- a/src/Entities/Player.cpp +++ b/src/Entities/Player.cpp @@ -170,6 +170,17 @@ cPlayer::~cPlayer(void) +void cPlayer::OnLoseSpectated() +{ + m_ClientHandle->SendCameraSetTo(*this); + m_ClientHandle->SendPlayerMoveLook(); + m_Spectating = nullptr; +} + + + + + int cPlayer::CalcLevelFromXp(int a_XpTotal) { // level 0 to 15 @@ -1304,18 +1315,23 @@ void cPlayer::SetGameMode(eGameMode a_GameMode) if (m_GameMode == a_GameMode) { - // Gamemode already set + // New gamemode unchanged, we're done: return; } - // Detach, if the player is switching from or to the spectator mode - if ((m_GameMode == gmSpectator) || (a_GameMode == gmSpectator)) + m_GameMode = a_GameMode; + UpdateCapabilities(); + + if (IsGameModeSpectator()) { + // Spectators cannot ride entities: Detach(); } - - m_GameMode = a_GameMode; - UpdateCapabilities(); + else + { + // Non-spectators may not spectate: + SpectateEntity(nullptr); + } m_ClientHandle->SendGameMode(a_GameMode); m_ClientHandle->SendInventorySlot(-1, -1, m_DraggingItem); @@ -1452,7 +1468,7 @@ void cPlayer::SendRotation(double a_YawDegrees, double a_PitchDegrees) -void cPlayer::SpectateEntity(const cEntity * a_Target) +void cPlayer::SpectateEntity(cEntity * a_Target) { if (a_Target == this) { @@ -1468,13 +1484,13 @@ void cPlayer::SpectateEntity(const cEntity * a_Target) if (a_Target == nullptr) { - m_ClientHandle->SendCameraSetTo(*this); - m_ClientHandle->SendPlayerMoveLook(); - m_Spectating = nullptr; + m_Spectating->OnLoseSpectator(*this); + OnLoseSpectated(); return; } m_Spectating = a_Target; + a_Target->OnAcquireSpectator(*this); m_ClientHandle->SendCameraSetTo(*a_Target); } @@ -3055,6 +3071,9 @@ void cPlayer::OnRemoveFromWorld(cWorld & a_World) // Remove any references to this player pointer by windows in the old world: CloseWindow(false); + // Stop spectation and remove our reference from the spectated: + SpectateEntity(nullptr); + // Remove the client handle from the world: m_World->RemoveClientFromChunks(m_ClientHandle.get()); diff --git a/src/Entities/Player.h b/src/Entities/Player.h index e0a88814d..2f2b9d62e 100644 --- a/src/Entities/Player.h +++ b/src/Entities/Player.h @@ -93,6 +93,9 @@ public: cPlayer(const std::shared_ptr & a_Client); virtual ~cPlayer() override; + /** Called when spectation stops, because the player crouched or when the entity we're spectating gets removed from the world. */ + void OnLoseSpectated(); + // tolua_begin /** Sets the experience total @@ -184,7 +187,7 @@ public: void SendRotation(double a_YawDegrees, double a_PitchDegrees); /** Spectates the target entity. If a_Target is nullptr or a pointer to self, end spectation. */ - void SpectateEntity(const cEntity * a_Target); + void SpectateEntity(cEntity * a_Target); /** Returns the position where projectiles thrown by this player should start, player eye position + adjustment */ Vector3d GetThrowStartPos(void) const; @@ -731,7 +734,7 @@ private: cTeam * m_Team; /** The entity that this player is spectating, nullptr if none. */ - const cEntity * m_Spectating; + cEntity * m_Spectating; StatisticsManager m_Stats; -- cgit v1.2.3