summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYehonal <yehonal.azeroth@gmail.com>2025-09-27 16:07:03 +0200
committerGitHub <noreply@github.com>2025-09-27 16:07:03 +0200
commit1736b7501f479723bba9fcfa4435e1b55e499d09 (patch)
tree6a4cb9f95b7b283969c98ffa569490796056a401
parentb950c610d4e41ee6bbfc9476020546548c20100d (diff)
refactor(Player): replace visibility detection calls with dedicated methods (#23025)
-rw-r--r--apps/test-framework/run-core-tests.sh3
-rw-r--r--src/server/game/Entities/Player/Player.cpp8
-rw-r--r--src/test/server/game/Commands/GmVisibleCommandTest.cpp218
3 files changed, 225 insertions, 4 deletions
diff --git a/apps/test-framework/run-core-tests.sh b/apps/test-framework/run-core-tests.sh
index 0f72fd572f..fb558149d7 100644
--- a/apps/test-framework/run-core-tests.sh
+++ b/apps/test-framework/run-core-tests.sh
@@ -3,6 +3,9 @@
# shellcheck source-path=SCRIPTDIR
CURRENT_PATH="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+# Clean up gcda files to avoid false positives in coverage reports
+find var/build/obj -name '*.gcda' -delete
+
# shellcheck source=../bash_shared/includes.sh
source "$CURRENT_PATH/../bash_shared/includes.sh"
diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp
index cc9a820fc7..02adb9ef0c 100644
--- a/src/server/game/Entities/Player/Player.cpp
+++ b/src/server/game/Entities/Player/Player.cpp
@@ -2243,7 +2243,7 @@ void Player::SetGameMaster(bool on)
CombatStopWithPets();
SetPhaseMask(uint32(PHASEMASK_ANYWHERE), false); // see and visible in all phases
- m_serverSideVisibilityDetect.SetValue(SERVERSIDE_VISIBILITY_GM, GetSession()->GetSecurity());
+ SetServerSideVisibilityDetect(SERVERSIDE_VISIBILITY_GM, GetSession()->GetSecurity());
}
else
{
@@ -2279,7 +2279,7 @@ void Player::SetGameMaster(bool on)
UpdateArea(m_areaUpdateId);
getHostileRefMgr().setOnlineOfflineState(true);
- m_serverSideVisibilityDetect.SetValue(SERVERSIDE_VISIBILITY_GM, SEC_PLAYER);
+ SetServerSideVisibilityDetect(SERVERSIDE_VISIBILITY_GM, SEC_PLAYER);
}
UpdateObjectVisibility();
@@ -2293,7 +2293,7 @@ void Player::SetGMVisible(bool on)
{
RemoveAurasDueToSpell(VISUAL_AURA);
m_ExtraFlags &= ~PLAYER_EXTRA_GM_INVISIBLE;
- m_serverSideVisibility.SetValue(SERVERSIDE_VISIBILITY_GM, SEC_PLAYER);
+ SetServerSideVisibility(SERVERSIDE_VISIBILITY_GM, SEC_PLAYER);
getHostileRefMgr().setOnlineOfflineState(false);
CombatStopWithPets();
@@ -2302,7 +2302,7 @@ void Player::SetGMVisible(bool on)
{
AddAura(VISUAL_AURA, this);
m_ExtraFlags |= PLAYER_EXTRA_GM_INVISIBLE;
- m_serverSideVisibility.SetValue(SERVERSIDE_VISIBILITY_GM, GetSession()->GetSecurity());
+ SetServerSideVisibility(SERVERSIDE_VISIBILITY_GM, GetSession()->GetSecurity());
}
}
diff --git a/src/test/server/game/Commands/GmVisibleCommandTest.cpp b/src/test/server/game/Commands/GmVisibleCommandTest.cpp
new file mode 100644
index 0000000000..8d4512ade5
--- /dev/null
+++ b/src/test/server/game/Commands/GmVisibleCommandTest.cpp
@@ -0,0 +1,218 @@
+/*
+ * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Affero General Public License as published by the
+ * Free Software Foundation; either version 3 of the License, or (at your
+ * option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "Player.h"
+#include "ScriptMgr.h"
+#include "WorldSession.h"
+#include "WorldMock.h"
+#include "ObjectGuid.h"
+#include "ScriptDefines/MiscScript.h"
+#include "ScriptDefines/PlayerScript.h"
+#include "ScriptDefines/WorldObjectScript.h"
+#include "ScriptDefines/UnitScript.h"
+#include "ScriptDefines/CommandScript.h"
+#include "SharedDefines.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include <string>
+#include <string_view>
+
+#ifndef TEST_F
+#define TEST_F(fixture, name) void fixture##_##name()
+#endif
+
+using namespace testing;
+
+namespace
+{
+class TestVisibilityScript : public PlayerScript
+{
+public:
+ TestVisibilityScript() : PlayerScript("TestVisibilityScript", { PLAYERHOOK_ON_SET_SERVER_SIDE_VISIBILITY }) { }
+
+ void OnPlayerSetServerSideVisibility(Player* player, ServerSideVisibilityType& type, AccountTypes& sec) override
+ {
+ ++CallCount;
+ LastPlayer = player;
+ LastType = type;
+ LastSecurity = sec;
+ }
+
+ static void EnsureRegistered()
+ {
+ if (!Instance)
+ Instance = new TestVisibilityScript();
+ }
+
+ static void Reset()
+ {
+ CallCount = 0;
+ LastPlayer = nullptr;
+ LastType = SERVERSIDE_VISIBILITY_GM;
+ LastSecurity = SEC_PLAYER;
+ }
+
+ inline static TestVisibilityScript* Instance = nullptr;
+ inline static uint32 CallCount = 0;
+ inline static Player* LastPlayer = nullptr;
+ inline static ServerSideVisibilityType LastType = SERVERSIDE_VISIBILITY_GM;
+ inline static AccountTypes LastSecurity = SEC_PLAYER;
+};
+
+class TestPlayer : public Player
+{
+public:
+ using Player::Player;
+
+ void UpdateObjectVisibility(bool /*forced*/ = true, bool /*fromUpdate*/ = false) override { }
+
+ void ForceInitValues(ObjectGuid::LowType guidLow = 1)
+ {
+ Object::_Create(guidLow, uint32(0), HighGuid::Player);
+ }
+};
+
+class GmVisibleCommandTest : public ::testing::Test
+{
+protected:
+ void SetUp() override
+ {
+ EnsureScriptRegistriesInitialized();
+
+ TestVisibilityScript::EnsureRegistered();
+
+ originalWorld = sWorld.release();
+ worldMock = new NiceMock<WorldMock>();
+ sWorld.reset(worldMock);
+
+ static std::string emptyString;
+ ON_CALL(*worldMock, GetDataPath()).WillByDefault(ReturnRef(emptyString));
+ ON_CALL(*worldMock, GetRealmName()).WillByDefault(ReturnRef(emptyString));
+ ON_CALL(*worldMock, GetDefaultDbcLocale()).WillByDefault(Return(LOCALE_enUS));
+ ON_CALL(*worldMock, getRate(_)).WillByDefault(Return(1.0f));
+ ON_CALL(*worldMock, getBoolConfig(_)).WillByDefault(Return(false));
+ ON_CALL(*worldMock, getIntConfig(_)).WillByDefault(Return(0));
+ ON_CALL(*worldMock, getFloatConfig(_)).WillByDefault(Return(0.0f));
+ ON_CALL(*worldMock, GetPlayerSecurityLimit()).WillByDefault(Return(SEC_PLAYER));
+
+ session = new WorldSession(1, "gm", 0, nullptr, SEC_GAMEMASTER, EXPANSION_WRATH_OF_THE_LICH_KING,
+ 0, LOCALE_enUS, 0, false, false, 0);
+
+ player = new TestPlayer(session);
+ player->ForceInitValues();
+ session->SetPlayer(player);
+ player->SetSession(session);
+
+ TestVisibilityScript::Reset();
+ }
+
+ void TearDown() override
+ {
+ // Intentional leaks of session/player to avoid database access in destructors.
+ IWorld* currentWorld = sWorld.release();
+ delete currentWorld;
+ worldMock = nullptr;
+
+ sWorld.reset(originalWorld);
+ originalWorld = nullptr;
+ session = nullptr;
+ player = nullptr;
+ }
+
+ void ExecuteCommand(std::string_view text)
+ {
+ if (text == ".gm visible off")
+ {
+ ApplyGmVisibleState(false);
+ }
+ else if (text == ".gm visible on")
+ {
+ ApplyGmVisibleState(true);
+ }
+ else
+ {
+ FAIL() << "Unsupported test command: " << text;
+ }
+ }
+
+ static void EnsureScriptRegistriesInitialized()
+ {
+ static bool initialized = false;
+ if (!initialized)
+ {
+ ScriptRegistry<MiscScript>::InitEnabledHooksIfNeeded(MISCHOOK_END);
+ ScriptRegistry<WorldObjectScript>::InitEnabledHooksIfNeeded(WORLDOBJECTHOOK_END);
+ ScriptRegistry<UnitScript>::InitEnabledHooksIfNeeded(UNITHOOK_END);
+ ScriptRegistry<PlayerScript>::InitEnabledHooksIfNeeded(PLAYERHOOK_END);
+ ScriptRegistry<CommandSC>::InitEnabledHooksIfNeeded(ALLCOMMANDHOOK_END);
+ initialized = true;
+ }
+ }
+
+ IWorld* originalWorld = nullptr;
+ NiceMock<WorldMock>* worldMock = nullptr;
+ WorldSession* session = nullptr;
+ TestPlayer* player = nullptr;
+
+private:
+ void ApplyGmVisibleState(bool makeVisible)
+ {
+ constexpr uint32 VISUAL_AURA = 37800;
+
+ if (makeVisible)
+ {
+ player->RemoveAurasDueToSpell(VISUAL_AURA);
+ player->SetGMVisible(true);
+ }
+ else
+ {
+ player->AddAura(VISUAL_AURA, player);
+ player->SetGMVisible(false);
+ }
+
+ player->UpdateObjectVisibility();
+ }
+};
+
+TEST_F(GmVisibleCommandTest, SetsPlayerInvisibleAndInvokesHook)
+{
+ ExecuteCommand(".gm visible off");
+
+ EXPECT_EQ(TestVisibilityScript::CallCount, 1u);
+ EXPECT_EQ(TestVisibilityScript::LastPlayer, player);
+ EXPECT_EQ(TestVisibilityScript::LastType, SERVERSIDE_VISIBILITY_GM);
+ EXPECT_EQ(TestVisibilityScript::LastSecurity, session->GetSecurity());
+ EXPECT_EQ(player->m_serverSideVisibility.GetValue(SERVERSIDE_VISIBILITY_GM), uint32(session->GetSecurity()));
+ EXPECT_FALSE(player->isGMVisible());
+}
+
+TEST_F(GmVisibleCommandTest, SetsPlayerVisibleAndInvokesHook)
+{
+ // Ensure the player starts from invisible state to test the opposite transition as well.
+ ExecuteCommand(".gm visible off");
+ TestVisibilityScript::Reset();
+
+ ExecuteCommand(".gm visible on");
+
+ EXPECT_EQ(TestVisibilityScript::CallCount, 1u);
+ EXPECT_EQ(TestVisibilityScript::LastPlayer, player);
+ EXPECT_EQ(TestVisibilityScript::LastType, SERVERSIDE_VISIBILITY_GM);
+ EXPECT_EQ(TestVisibilityScript::LastSecurity, SEC_PLAYER);
+ EXPECT_EQ(player->m_serverSideVisibility.GetValue(SERVERSIDE_VISIBILITY_GM), uint32(SEC_PLAYER));
+ EXPECT_TRUE(player->isGMVisible());
+}
+}