diff --git a/apps/ci/ci-install-modules.sh b/apps/ci/ci-install-modules.sh index 5e98469c7..b3271de3c 100755 --- a/apps/ci/ci-install-modules.sh +++ b/apps/ci/ci-install-modules.sh @@ -28,3 +28,4 @@ git clone --depth=1 --branch=master https://github.com/azerothcore/mod-server-au git clone --depth=1 --branch=master https://github.com/azerothcore/mod-transmog.git modules/mod-transmog git clone --depth=1 --branch=main https://github.com/azerothcore/mod-progression-system.git modules/mod-progression-system git clone --depth=1 --branch=master https://github.com/azerothcore/mod-arena-3v3-solo-queue.git modules/mod-arena-3v3-solo-queue +git clone --depth=1 --branch=master https://github.com/azerothcore/mod-costumes.git modules/mod-costumes diff --git a/data/sql/updates/db_world/2025_02_19_01.sql b/data/sql/updates/db_world/2025_02_19_01.sql new file mode 100644 index 000000000..6e2dfd8f9 --- /dev/null +++ b/data/sql/updates/db_world/2025_02_19_01.sql @@ -0,0 +1,36 @@ +-- DB update 2025_02_19_00 -> 2025_02_19_01 +-- Bjorn Halgurdsson - Set position +UPDATE `creature` SET `position_x` = 1518.61, `position_y` = -5249.85, `position_z` = 215.38, `orientation` = 5.41052, `VerifiedBuild` = 59069 WHERE `guid` = 112513 AND `id1` = 24238; + +-- Bjorn Halgurdsson - Set speed_run +UPDATE `creature_template` SET `speed_run` = 1.7435 WHERE (`entry` = 24238); + +-- Bjorn Halgurdsson - Set mount +DELETE FROM `creature_addon` WHERE (`guid` IN (112513)); +INSERT INTO `creature_addon` (`guid`, `path_id`, `mount`, `bytes1`, `bytes2`, `emote`, `visibilityDistanceType`, `auras`) VALUES +(112513, 0, 22657, 0, 0, 0, 0, NULL); + +-- Bjorn Halgurdsson - Set movement +UPDATE `creature_template_movement` SET `Swim` = 0, `Flight` = 0 WHERE (`CreatureId` = 24238); + +-- Bjorn Halgurdsson - SmartAI +UPDATE `creature_template` SET `AIName` = 'SmartAI' WHERE `entry` = 24238; + +DELETE FROM `smart_scripts` WHERE (`source_type` = 0 AND `entryorguid` = 24238); +INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `event_param6`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES +(24238, 0, 0, 0, 1, 0, 100, 0, 10000, 15000, 45000, 60000, 0, 0, 1, 2, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Bjorn Halgurdsson - Out of Combat - Say Line 2'), +(24238, 0, 1, 2, 8, 0, 100, 0, 43315, 0, 0, 0, 0, 0, 84, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Bjorn Halgurdsson - On Spellhit \'Vrykul Insult\' - Say Line 0'), +(24238, 0, 2, 3, 61, 0, 100, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Bjorn Halgurdsson - On Spellhit \'Vrykul Insult\' - Say Line 1'), +(24238, 0, 3, 4, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 43, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Bjorn Halgurdsson - On Spellhit \'Vrykul Insult\' - Dismount'), +(24238, 0, 4, 5, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 207, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Bjorn Halgurdsson - On Spellhit \'Vrykul Insult\' - Set hover 0'), +(24238, 0, 5, 6, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 60, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Bjorn Halgurdsson - On Spellhit \'Vrykul Insult\' - Set Fly Off'), +(24238, 0, 6, 7, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 8, 2, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Bjorn Halgurdsson - On Spellhit \'Vrykul Insult\' - Set Reactstate Aggressive'), +(24238, 0, 7, 8, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 19, 2, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Bjorn Halgurdsson - On Spellhit \'Vrykul Insult\' - Remove Flags Not Attackable'), +(24238, 0, 8, 0, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 49, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 'Bjorn Halgurdsson - On Spellhit \'Vrykul Insult\' - Start Attacking'), +(24238, 0, 9, 0, 6, 0, 100, 0, 0, 0, 0, 0, 0, 0, 11, 43371, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Bjorn Halgurdsson - On Just Died - Cast \'Bjorn Kill Credit\''), +(24238, 0, 10, 0, 0, 0, 100, 0, 5000, 10000, 10000, 15000, 0, 0, 11, 32736, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Bjorn Halgurdsson - In Combat - Cast \'Mortal Strike\''), +(24238, 0, 11, 0, 0, 0, 100, 0, 0, 5000, 10000, 15000, 0, 0, 11, 33661, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 'Bjorn Halgurdsson - In Combat - Cast \'Crush Armor\''), +(24238, 0, 12, 13, 25, 0, 100, 512, 0, 0, 0, 0, 0, 0, 60, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Bjorn Halgurdsson - On Reset - Set Fly On'), +(24238, 0, 13, 14, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 207, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Bjorn Halgurdsson - On Reset - Set hover 1'), +(24238, 0, 14, 15, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Bjorn Halgurdsson - On Reset - Set Reactstate Passive'), +(24238, 0, 15, 0, 61, 0, 100, 512, 0, 0, 0, 0, 0, 0, 18, 2, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Bjorn Halgurdsson - On Reset - Set Flags Not Attackable'); diff --git a/data/sql/updates/db_world/2025_02_21_00.sql b/data/sql/updates/db_world/2025_02_21_00.sql new file mode 100644 index 000000000..86fb9a021 --- /dev/null +++ b/data/sql/updates/db_world/2025_02_21_00.sql @@ -0,0 +1,112 @@ +-- DB update 2025_02_19_01 -> 2025_02_21_00 +DROP TABLE IF EXISTS `antidos_opcode_policies`; +CREATE TABLE `antidos_opcode_policies` ( + `Opcode` smallint unsigned NOT NULL, + `Policy` tinyint unsigned NOT NULL, + `MaxAllowedCount` smallint unsigned NOT NULL, + PRIMARY KEY (`Opcode`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; + +INSERT INTO `antidos_opcode_policies` (`Opcode`, `Policy`, `MaxAllowedCount`) VALUES + (393, 1, 200), + (404, 1, 200), + (398, 1, 200), + (102, 1, 200), + (1217, 1, 200), + (643, 1, 200), + (642, 1, 200), + (98, 1, 200), + (1192, 1, 200), + (1218, 1, 200), + (238, 1, 200), + (564, 1, 50), + (565, 1, 50), + (107, 1, 50), + (1065, 1, 50), + (999, 1, 50), + (1131, 1, 50), + (1153, 1, 50), + (177, 1, 50), + (450, 1, 50), + (483, 1, 25), + (1282, 1, 20), + (1016, 1, 20), + (1162, 1, 20), + (1133, 1, 20), + (448, 1, 10), + (452, 1, 10), + (638, 1, 10), + (454, 1, 10), + (1272, 1, 10), + (1139, 1, 10), + (1241, 1, 10), + (56, 1, 10), + (106, 1, 10), + (105, 1, 10), + (711, 1, 10), + (810, 1, 10), + (458, 1, 10), + (120, 1, 10), + (654, 1, 10), + (655, 1, 10), + (445, 1, 10), + (1179, 1, 10), + (1143, 1, 10), + (1144, 1, 10), + (1145, 1, 10), + (1142, 1, 10), + (1193, 1, 10), + (1204, 1, 10), + (839, 1, 10), + (467, 1, 10), + (996, 1, 10), + (600, 4, 5), + (612, 4, 5), + (601, 4, 5), + (54, 1, 3), + (55, 1, 3), + (517, 1, 3), + (519, 1, 3), + (535, 1, 3), + (1264, 1, 3), + (1069, 1, 3), + (1070, 1, 3), + (1071, 1, 3), + (1072, 1, 3), + (1073, 1, 3), + (1210, 1, 3), + (1074, 1, 3), + (1075, 1, 3), + (1077, 1, 3), + (847, 1, 3), + (849, 1, 3), + (850, 1, 3), + (851, 1, 3), + (853, 1, 3), + (852, 1, 3), + (854, 1, 3), + (122, 1, 3), + (130, 1, 3), + (132, 1, 3), + (133, 1, 3), + (141, 1, 3), + (143, 1, 3), + (144, 1, 3), + (145, 1, 3), + (561, 1, 3), + (562, 1, 3), + (563, 1, 3), + (764, 1, 3), + (1004, 1, 3), + (1005, 1, 3), + (1002, 1, 3), + (1003, 1, 3), + (1035, 1, 3), + (497, 1, 3), + (705, 1, 3), + (682, 1, 3), + (809, 1, 3), + (1259, 1, 3), + (910, 1, 3), + (802, 1, 3), + (1203, 1, 150); diff --git a/data/sql/updates/db_world/2025_02_21_01.sql b/data/sql/updates/db_world/2025_02_21_01.sql new file mode 100644 index 000000000..808e316c7 --- /dev/null +++ b/data/sql/updates/db_world/2025_02_21_01.sql @@ -0,0 +1,8 @@ +-- DB update 2025_02_21_00 -> 2025_02_21_01 +-- +DELETE FROM `spell_script_names` WHERE `ScriptName` IN +('spell_eredar_twins_handle_dark_touched_periodic', 'spell_eredar_twins_handle_flame_touched_periodic', 'spell_eredar_twins_handle_flame_touched_flame_sear'); +INSERT INTO `spell_script_names` (`spell_id`, `ScriptName`) VALUES +(45342, 'spell_eredar_twins_handle_flame_touched_periodic'), +(45271, 'spell_eredar_twins_handle_dark_touched_periodic'), +(46771, 'spell_eredar_twins_handle_flame_touched_flame_sear'); diff --git a/data/sql/updates/db_world/2025_02_21_02.sql b/data/sql/updates/db_world/2025_02_21_02.sql new file mode 100644 index 000000000..d71febcdd --- /dev/null +++ b/data/sql/updates/db_world/2025_02_21_02.sql @@ -0,0 +1,5 @@ +-- DB update 2025_02_21_01 -> 2025_02_21_02 +-- +DELETE FROM `smart_scripts` WHERE (`entryorguid` = 24666) AND (`source_type` = 0) AND (`id` IN (4)); +INSERT INTO `smart_scripts` (`entryorguid`, `source_type`, `id`, `link`, `event_type`, `event_phase_mask`, `event_chance`, `event_flags`, `event_param1`, `event_param2`, `event_param3`, `event_param4`, `event_param5`, `event_param6`, `action_type`, `action_param1`, `action_param2`, `action_param3`, `action_param4`, `action_param5`, `action_param6`, `target_type`, `target_param1`, `target_param2`, `target_param3`, `target_param4`, `target_x`, `target_y`, `target_z`, `target_o`, `comment`) VALUES +(24666, 0, 4, 0, 37, 0, 100, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 'Flame Strike Trigger (Kael - 5Man) - On Initialize - Set Reactstate Passive'); diff --git a/data/sql/updates/db_world/2025_02_21_03.sql b/data/sql/updates/db_world/2025_02_21_03.sql new file mode 100644 index 000000000..f0152a476 --- /dev/null +++ b/data/sql/updates/db_world/2025_02_21_03.sql @@ -0,0 +1,4 @@ +-- DB update 2025_02_21_02 -> 2025_02_21_03 +-- +UPDATE `creature_onkill_reputation` SET `RewOnKillRepValue1` = 120 WHERE (`creature_id` = 24664); +UPDATE `creature_onkill_reputation` SET `MaxStanding1` = 7 WHERE `creature_id` IN (24723, 24560, 24664, 24744); diff --git a/data/sql/updates/db_world/2025_02_22_00.sql b/data/sql/updates/db_world/2025_02_22_00.sql new file mode 100644 index 000000000..754270606 --- /dev/null +++ b/data/sql/updates/db_world/2025_02_22_00.sql @@ -0,0 +1,3 @@ +-- DB update 2025_02_21_03 -> 2025_02_22_00 +-- +UPDATE `creature_template_movement` SET `Flight` = 2 WHERE `CreatureId` = 24418; diff --git a/src/server/apps/worldserver/worldserver.conf.dist b/src/server/apps/worldserver/worldserver.conf.dist index 98cbc3970..17fd68060 100644 --- a/src/server/apps/worldserver/worldserver.conf.dist +++ b/src/server/apps/worldserver/worldserver.conf.dist @@ -1120,17 +1120,6 @@ PreventAFKLogout = 0 ################################################################################################### # PACKET SPOOF PROTECTION SETTINGS -# -# These settings determine which action to take when harmful packet spoofing is detected. -# -# PacketSpoof.Policy -# Description: Determines the course of action when packet spoofing is detected. -# Values: 0 - Log only -# 1 - Log + kick -# 2 - Log + kick + ban - -PacketSpoof.Policy = 1 - # # PacketSpoof.BanMode # Description: If PacketSpoof.Policy equals 2, this will determine the ban mode. diff --git a/src/server/game/Globals/WorldGlobals.cpp b/src/server/game/Globals/WorldGlobals.cpp new file mode 100644 index 000000000..fcfb3369e --- /dev/null +++ b/src/server/game/Globals/WorldGlobals.cpp @@ -0,0 +1,76 @@ +/* + * 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 . + */ + +#include "DatabaseEnv.h" +#include "Log.h" +#include "QueryResult.h" +#include "Timer.h" +#include "WorldGlobals.h" + +WorldGlobals* WorldGlobals::instance() +{ + static WorldGlobals instance; + return &instance; +} + +void WorldGlobals::LoadAntiDosOpcodePolicies() +{ + uint32 oldMSTime = getMSTime(); + + _antiDosOpcodePolicies = {}; + + QueryResult result = WorldDatabase.Query("SELECT Opcode, Policy, MaxAllowedCount FROM antidos_opcode_policies"); + if (!result) + { + LOG_WARN("server.loading", ">> Loaded 0 AntiDos Opcode Policies. DB table `antidos_opcode_policies` is empty!"); + LOG_INFO("server.loading", " "); + return; + } + + uint32 count = 0; + + do + { + Field* fields = result->Fetch(); + + uint16 opcode = fields[0].Get(); + if (opcode >= NUM_OPCODE_HANDLERS) + { + LOG_ERROR("server.loading", "Unkown opcode {} in table `antidos_opcode_policies`, skipping.", opcode); + continue; + } + + std::unique_ptr policy = std::make_unique(); + policy->Policy = fields[1].Get(); + policy->MaxAllowedCount = fields[2].Get(); + + _antiDosOpcodePolicies[opcode] = std::move(policy); + + ++count; + } while (result->NextRow()); + + LOG_INFO("server.loading", ">> Loaded {} AntiDos Opcode Policies in {} ms", count, GetMSTimeDiffToNow(oldMSTime)); + LOG_INFO("server.loading", " "); +} + +AntiDosOpcodePolicy const* WorldGlobals::GetAntiDosPolicyForOpcode(uint16 opcode) +{ + if (opcode >= NUM_OPCODE_HANDLERS) + return nullptr; + + return _antiDosOpcodePolicies[opcode].get(); +} diff --git a/src/server/game/Globals/WorldGlobals.h b/src/server/game/Globals/WorldGlobals.h new file mode 100644 index 000000000..e8695d02b --- /dev/null +++ b/src/server/game/Globals/WorldGlobals.h @@ -0,0 +1,44 @@ +/* + * 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 . + */ + +#ifndef __WORLDGLOBALS_H +#define __WORLDGLOBALS_H + +#include "Common.h" +#include "Opcodes.h" + +struct AntiDosOpcodePolicy +{ + uint8 Policy; + uint16 MaxAllowedCount; +}; + +class WorldGlobals +{ +public: + static WorldGlobals* instance(); + + void LoadAntiDosOpcodePolicies(); + AntiDosOpcodePolicy const* GetAntiDosPolicyForOpcode(uint16 opcode); + +private: + std::array, NUM_OPCODE_HANDLERS> _antiDosOpcodePolicies; +}; + +#define sWorldGlobals WorldGlobals::instance() + +#endif diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp index c54bcab8d..823d52976 100644 --- a/src/server/game/Maps/Map.cpp +++ b/src/server/game/Maps/Map.cpp @@ -2212,15 +2212,6 @@ bool InstanceMap::AddPlayerToMap(Player* player) m_resetAfterUnload = false; m_unloadWhenEmpty = false; - if (instance_data && instance_data->IsTwoFactionInstance() - && instance_data->GetTeamIdInInstance() == TEAM_NEUTRAL) - { - instance_data->SetTeamIdInInstance(player->GetTeamId()); - if (Group* group = player->GetGroup()) - if (Player* leader = ObjectAccessor::FindConnectedPlayer(group->GetLeaderGUID())) - instance_data->SetTeamIdInInstance(leader->GetTeamId()); - } - // this will acquire the same mutex so it cannot be in the previous block Map::AddPlayerToMap(player); diff --git a/src/server/game/Maps/MapInstanced.cpp b/src/server/game/Maps/MapInstanced.cpp index 8b42389e3..9d8172a73 100644 --- a/src/server/game/Maps/MapInstanced.cpp +++ b/src/server/game/Maps/MapInstanced.cpp @@ -147,7 +147,7 @@ Map* MapInstanced::CreateInstanceForPlayer(const uint32 mapId, Player* player) map = FindInstanceMap(destInstId); if (!map) - map = CreateInstance(destInstId, pSave, realdiff); + map = CreateInstance(destInstId, pSave, realdiff, player); else if (IsSharedDifficultyMap(mapId) && !map->HavePlayers() && map->GetDifficulty() != realdiff) { if (player->isBeingLoaded()) // pussywizard: crashfix (assert(passengers.empty) fail in ~transport), could be added to a transport during loading from db @@ -160,7 +160,7 @@ Map* MapInstanced::CreateInstanceForPlayer(const uint32 mapId, Player* player) if (i->first == destInstId) { DestroyInstance(i); - map = CreateInstance(destInstId, pSave, realdiff); + map = CreateInstance(destInstId, pSave, realdiff, player); break; } } @@ -170,14 +170,14 @@ Map* MapInstanced::CreateInstanceForPlayer(const uint32 mapId, Player* player) uint32 newInstanceId = sMapMgr->GenerateInstanceId(); ASSERT(!FindInstanceMap(newInstanceId)); // pussywizard: instance with new id can't exist Difficulty diff = player->GetGroup() ? player->GetGroup()->GetDifficulty(IsRaid()) : player->GetDifficulty(IsRaid()); - map = CreateInstance(newInstanceId, nullptr, diff); + map = CreateInstance(newInstanceId, nullptr, diff, player); } } return map; } -InstanceMap* MapInstanced::CreateInstance(uint32 InstanceId, InstanceSave* save, Difficulty difficulty) +InstanceMap* MapInstanced::CreateInstance(uint32 InstanceId, InstanceSave* save, Difficulty difficulty, Player* player) { // load/create a map std::lock_guard guard(Lock); @@ -213,6 +213,16 @@ InstanceMap* MapInstanced::CreateInstance(uint32 InstanceId, InstanceSave* save, else map->CreateInstanceScript(false, "", 0); + if (map->GetInstanceScript() && map->GetInstanceScript()->IsTwoFactionInstance() + && map->GetInstanceScript()->GetTeamIdInInstance() == TEAM_NEUTRAL) + { + ASSERT(player); // Player should exist, as checked by in MapInstanced::CreateInstanceForPlayer + map->GetInstanceScript()->SetTeamIdInInstance(player->GetTeamId()); + if (Group* group = player->GetGroup()) + if (Player* leader = ObjectAccessor::FindConnectedPlayer(group->GetLeaderGUID())) + map->GetInstanceScript()->SetTeamIdInInstance(leader->GetTeamId()); + } + map->OnCreateMap(); if (!save) // this is for sure a dungeon (assert above), no need to check here diff --git a/src/server/game/Maps/MapInstanced.h b/src/server/game/Maps/MapInstanced.h index f08c5687a..e50057c9e 100644 --- a/src/server/game/Maps/MapInstanced.h +++ b/src/server/game/Maps/MapInstanced.h @@ -50,7 +50,7 @@ public: void InitVisibilityDistance() override; private: - InstanceMap* CreateInstance(uint32 InstanceId, InstanceSave* save, Difficulty difficulty); + InstanceMap* CreateInstance(uint32 InstanceId, InstanceSave* save, Difficulty difficulty, Player* player); BattlegroundMap* CreateBattleground(uint32 InstanceId, Battleground* bg); InstancedMaps m_InstancedMaps; diff --git a/src/server/game/Scripting/ScriptDefines/PlayerScript.cpp b/src/server/game/Scripting/ScriptDefines/PlayerScript.cpp index 422a397df..bf3641d35 100644 --- a/src/server/game/Scripting/ScriptDefines/PlayerScript.cpp +++ b/src/server/game/Scripting/ScriptDefines/PlayerScript.cpp @@ -214,18 +214,16 @@ void ScriptMgr::OnPlayerBeforeUpdate(Player* player, uint32 p_time) CALL_ENABLED_HOOKS(PlayerScript, PLAYERHOOK_ON_BEFORE_UPDATE, script->OnPlayerBeforeUpdate(player, p_time)); } +void ScriptMgr::OnPlayerAfterUpdate(Player* player, uint32 p_time) +{ + CALL_ENABLED_HOOKS(PlayerScript, PLAYERHOOK_ON_AFTER_UPDATE, script->OnPlayerAfterUpdate(player, p_time)); +} + void ScriptMgr::OnPlayerUpdate(Player* player, uint32 p_time) { CALL_ENABLED_HOOKS(PlayerScript, PLAYERHOOK_ON_UPDATE, script->OnPlayerUpdate(player, p_time)); } -void ScriptMgr::OnPlayerAfterUpdate(Player* player, uint32 diff) -{ - ExecuteScript([&](PlayerScript* script) - { - script->OnPlayerAfterUpdate(player, diff); - }); -} void ScriptMgr::OnPlayerLogin(Player* player) { diff --git a/src/server/game/Scripting/ScriptDefines/PlayerScript.h b/src/server/game/Scripting/ScriptDefines/PlayerScript.h index c640e5e6e..47c266396 100644 --- a/src/server/game/Scripting/ScriptDefines/PlayerScript.h +++ b/src/server/game/Scripting/ScriptDefines/PlayerScript.h @@ -45,6 +45,7 @@ enum PlayerHook PLAYERHOOK_ON_AFTER_SPEC_SLOT_CHANGED, PLAYERHOOK_ON_BEFORE_UPDATE, PLAYERHOOK_ON_UPDATE, + PLAYERHOOK_ON_AFTER_UPDATE, PLAYERHOOK_ON_MONEY_CHANGED, PLAYERHOOK_ON_BEFORE_LOOT_MONEY, PLAYERHOOK_ON_GIVE_EXP, diff --git a/src/server/game/Server/WorldSession.cpp b/src/server/game/Server/WorldSession.cpp index fd7c86713..dab404a86 100644 --- a/src/server/game/Server/WorldSession.cpp +++ b/src/server/game/Server/WorldSession.cpp @@ -49,6 +49,7 @@ #include "Vehicle.h" #include "WardenWin.h" #include "World.h" +#include "WorldGlobals.h" #include "WorldPacket.h" #include "WorldSocket.h" #include "WorldState.h" @@ -343,145 +344,150 @@ bool WorldSession::Update(uint32 diff, PacketFilter& updater) METRIC_DETAILED_TIMER("worldsession_update_opcode_time", METRIC_TAG("opcode", opHandle->Name)); LOG_DEBUG("network", "message id {} ({}) under READ", opcode, opHandle->Name); - try + WorldSession::DosProtection::Policy const evaluationPolicy = AntiDOS.EvaluateOpcode(*packet, currentTime); + switch (evaluationPolicy) { - switch (opHandle->Status) - { - case STATUS_LOGGEDIN: - if (!_player) - { - // skip STATUS_LOGGEDIN opcode unexpected errors if player logout sometime ago - this can be network lag delayed packets - //! If player didn't log out a while ago, it means packets are being sent while the server does not recognize - //! the client to be in world yet. We will re-add the packets to the bottom of the queue and process them later. - if (!m_playerRecentlyLogout) - { - requeuePackets.push_back(packet); - deletePacket = false; + case WorldSession::DosProtection::Policy::Kick: + case WorldSession::DosProtection::Policy::Ban: + processedPackets = MAX_PROCESSED_PACKETS_IN_SAME_WORLDSESSION_UPDATE; + break; + case WorldSession::DosProtection::Policy::BlockingThrottle: + requeuePackets.push_back(packet); + deletePacket = false; + processedPackets = MAX_PROCESSED_PACKETS_IN_SAME_WORLDSESSION_UPDATE; + break; + default: + break; + } - LOG_DEBUG("network", "Delaying processing of message with status STATUS_LOGGEDIN: No players in the world for account id {}", GetAccountId()); - } - } - else if (_player->IsInWorld()) + if (evaluationPolicy == WorldSession::DosProtection::Policy::Process + || evaluationPolicy == WorldSession::DosProtection::Policy::Log) + { + try + { + switch (opHandle->Status) { - if (AntiDOS.EvaluateOpcode(*packet, currentTime)) + case STATUS_LOGGEDIN: + if (!_player) + { + // skip STATUS_LOGGEDIN opcode unexpected errors if player logout sometime ago - this can be network lag delayed packets + //! If player didn't log out a while ago, it means packets are being sent while the server does not recognize + //! the client to be in world yet. We will re-add the packets to the bottom of the queue and process them later. + if (!m_playerRecentlyLogout) + { + requeuePackets.push_back(packet); + deletePacket = false; + + LOG_DEBUG("network", "Delaying processing of message with status STATUS_LOGGEDIN: No players in the world for account id {}", GetAccountId()); + } + } + else if (_player->IsInWorld()) { if (!sScriptMgr->CanPacketReceive(this, *packet)) - { break; - } opHandle->Call(this, *packet); LogUnprocessedTail(packet); +#ifdef MOD_PLAYERBOTS sScriptMgr->OnPacketReceived(this, *packet); +#endif + } + + // lag can cause STATUS_LOGGEDIN opcodes to arrive after the player started a transfer + break; + case STATUS_LOGGEDIN_OR_RECENTLY_LOGGOUT: + if (!_player && !m_playerRecentlyLogout) // There's a short delay between _player = null and m_playerRecentlyLogout = true during logout + { + LogUnexpectedOpcode(packet, "STATUS_LOGGEDIN_OR_RECENTLY_LOGGOUT", + "the player has not logged in yet and not recently logout"); } else - processedPackets = MAX_PROCESSED_PACKETS_IN_SAME_WORLDSESSION_UPDATE; // break out of packet processing loop - } - - // lag can cause STATUS_LOGGEDIN opcodes to arrive after the player started a transfer - break; - case STATUS_LOGGEDIN_OR_RECENTLY_LOGGOUT: - if (!_player && !m_playerRecentlyLogout) // There's a short delay between _player = null and m_playerRecentlyLogout = true during logout - { - LogUnexpectedOpcode(packet, "STATUS_LOGGEDIN_OR_RECENTLY_LOGGOUT", - "the player has not logged in yet and not recently logout"); - } - else if (AntiDOS.EvaluateOpcode(*packet, currentTime)) - { - // not expected _player or must checked in packet hanlder - if (!sScriptMgr->CanPacketReceive(this, *packet)) - break; - - opHandle->Call(this, *packet); - LogUnprocessedTail(packet); - - sScriptMgr->OnPacketReceived(this, *packet); - } - else - processedPackets = MAX_PROCESSED_PACKETS_IN_SAME_WORLDSESSION_UPDATE; // break out of packet processing loop - break; - case STATUS_TRANSFER: - if (_player && !_player->IsInWorld() && AntiDOS.EvaluateOpcode(*packet, currentTime)) - { - if (!sScriptMgr->CanPacketReceive(this, *packet)) { - break; + // not expected _player or must checked in packet hanlder + if (!sScriptMgr->CanPacketReceive(this, *packet)) + break; + + opHandle->Call(this, *packet); + LogUnprocessedTail(packet); +#ifdef MOD_PLAYERBOTS + sScriptMgr->OnPacketReceived(this, *packet); +#endif } - - opHandle->Call(this, *packet); - LogUnprocessedTail(packet); - - sScriptMgr->OnPacketReceived(this, *packet); - } - else - processedPackets = MAX_PROCESSED_PACKETS_IN_SAME_WORLDSESSION_UPDATE; // break out of packet processing loop - break; - case STATUS_AUTHED: - if (m_inQueue) // prevent cheating break; - - // some auth opcodes can be recieved before STATUS_LOGGEDIN_OR_RECENTLY_LOGGOUT opcodes - // however when we recieve CMSG_CHAR_ENUM we are surely no longer during the logout process. - if (packet->GetOpcode() == CMSG_CHAR_ENUM) - m_playerRecentlyLogout = false; - - if (AntiDOS.EvaluateOpcode(*packet, currentTime)) - { - if (!sScriptMgr->CanPacketReceive(this, *packet)) + case STATUS_TRANSFER: + if (_player && !_player->IsInWorld()) { - break; + if (!sScriptMgr->CanPacketReceive(this, *packet)) + break; + + opHandle->Call(this, *packet); + LogUnprocessedTail(packet); +#ifdef MOD_PLAYERBOTS + sScriptMgr->OnPacketReceived(this, *packet); +#endif } + break; + case STATUS_AUTHED: + if (m_inQueue) // prevent cheating + break; + + // some auth opcodes can be recieved before STATUS_LOGGEDIN_OR_RECENTLY_LOGGOUT opcodes + // however when we recieve CMSG_CHAR_ENUM we are surely no longer during the logout process. + if (packet->GetOpcode() == CMSG_CHAR_ENUM) + m_playerRecentlyLogout = false; + + if (!sScriptMgr->CanPacketReceive(this, *packet)) + break; opHandle->Call(this, *packet); LogUnprocessedTail(packet); - +#ifdef MOD_PLAYERBOTS sScriptMgr->OnPacketReceived(this, *packet); +#endif + break; + case STATUS_NEVER: + LOG_ERROR("network.opcode", "Received not allowed opcode {} from {}", + GetOpcodeNameForLogging(static_cast(packet->GetOpcode())), GetPlayerInfo()); + break; + case STATUS_UNHANDLED: + LOG_DEBUG("network.opcode", "Received not handled opcode {} from {}", + GetOpcodeNameForLogging(static_cast(packet->GetOpcode())), GetPlayerInfo()); + break; } - else - processedPackets = MAX_PROCESSED_PACKETS_IN_SAME_WORLDSESSION_UPDATE; // break out of packet processing loop - break; - case STATUS_NEVER: - LOG_ERROR("network.opcode", "Received not allowed opcode {} from {}", - GetOpcodeNameForLogging(static_cast(packet->GetOpcode())), GetPlayerInfo()); - break; - case STATUS_UNHANDLED: - LOG_DEBUG("network.opcode", "Received not handled opcode {} from {}", - GetOpcodeNameForLogging(static_cast(packet->GetOpcode())), GetPlayerInfo()); - break; } - } - catch (WorldPackets::InvalidHyperlinkException const& ihe) - { - LOG_ERROR("network", "{} sent {} with an invalid link:\n{}", GetPlayerInfo(), - GetOpcodeNameForLogging(static_cast(packet->GetOpcode())), ihe.GetInvalidValue()); + catch (WorldPackets::InvalidHyperlinkException const& ihe) + { + LOG_ERROR("network", "{} sent {} with an invalid link:\n{}", GetPlayerInfo(), + GetOpcodeNameForLogging(static_cast(packet->GetOpcode())), ihe.GetInvalidValue()); - if (sWorld->getIntConfig(CONFIG_CHAT_STRICT_LINK_CHECKING_KICK)) - { - KickPlayer("WorldSession::Update Invalid chat link"); + if (sWorld->getIntConfig(CONFIG_CHAT_STRICT_LINK_CHECKING_KICK)) + { + KickPlayer("WorldSession::Update Invalid chat link"); + } } - } - catch (WorldPackets::IllegalHyperlinkException const& ihe) - { - LOG_ERROR("network", "{} sent {} which illegally contained a hyperlink:\n{}", GetPlayerInfo(), - GetOpcodeNameForLogging(static_cast(packet->GetOpcode())), ihe.GetInvalidValue()); + catch (WorldPackets::IllegalHyperlinkException const& ihe) + { + LOG_ERROR("network", "{} sent {} which illegally contained a hyperlink:\n{}", GetPlayerInfo(), + GetOpcodeNameForLogging(static_cast(packet->GetOpcode())), ihe.GetInvalidValue()); - if (sWorld->getIntConfig(CONFIG_CHAT_STRICT_LINK_CHECKING_KICK)) - { - KickPlayer("WorldSession::Update Illegal chat link"); + if (sWorld->getIntConfig(CONFIG_CHAT_STRICT_LINK_CHECKING_KICK)) + { + KickPlayer("WorldSession::Update Illegal chat link"); + } } - } - catch (WorldPackets::PacketArrayMaxCapacityException const& pamce) - { - LOG_ERROR("network", "PacketArrayMaxCapacityException: {} while parsing {} from {}.", - pamce.what(), GetOpcodeNameForLogging(static_cast(packet->GetOpcode())), GetPlayerInfo()); - } - catch (ByteBufferException const&) - { - LOG_ERROR("network", "WorldSession::Update ByteBufferException occured while parsing a packet (opcode: {}) from client {}, accountid={}. Skipped packet.", packet->GetOpcode(), GetRemoteAddress(), GetAccountId()); - if (sLog->ShouldLog("network", LogLevel::LOG_LEVEL_DEBUG)) + catch (WorldPackets::PacketArrayMaxCapacityException const& pamce) { - LOG_DEBUG("network", "Dumping error causing packet:"); - packet->hexlike(); + LOG_ERROR("network", "PacketArrayMaxCapacityException: {} while parsing {} from {}.", + pamce.what(), GetOpcodeNameForLogging(static_cast(packet->GetOpcode())), GetPlayerInfo()); + } + catch (ByteBufferException const&) + { + LOG_ERROR("network", "WorldSession::Update ByteBufferException occured while parsing a packet (opcode: {}) from client {}, accountid={}. Skipped packet.", packet->GetOpcode(), GetRemoteAddress(), GetAccountId()); + if (sLog->ShouldLog("network", LogLevel::LOG_LEVEL_DEBUG)) + { + LOG_DEBUG("network", "Dumping error causing packet:"); + packet->hexlike(); + } } } @@ -1352,14 +1358,17 @@ Warden* WorldSession::GetWarden() return &(*_warden); } -bool WorldSession::DosProtection::EvaluateOpcode(WorldPacket& p, time_t time) const +WorldSession::DosProtection::Policy WorldSession::DosProtection::EvaluateOpcode(WorldPacket const& p, time_t const time) const { - uint32 maxPacketCounterAllowed = GetMaxPacketCounterAllowed(p.GetOpcode()); + AntiDosOpcodePolicy const* policy = sWorldGlobals->GetAntiDosPolicyForOpcode(p.GetOpcode()); + if (!policy) + return WorldSession::DosProtection::Policy::Process; // Return true if there is no policy for the opcode - // Return true if there no limit for the opcode + uint32 const maxPacketCounterAllowed = policy->MaxAllowedCount; if (!maxPacketCounterAllowed) - return true; + return WorldSession::DosProtection::Policy::Process; // Return true if there no limit for the opcode + // packetCounter is opcodes handled in the same world second, so MaxAllowedCount is per second PacketCounter& packetCounter = _PacketThrottlingMap[p.GetOpcode()]; if (packetCounter.lastReceiveTime != time) { @@ -1369,298 +1378,57 @@ bool WorldSession::DosProtection::EvaluateOpcode(WorldPacket& p, time_t time) co // Check if player is flooding some packets if (++packetCounter.amountCounter <= maxPacketCounterAllowed) - return true; + return WorldSession::DosProtection::Policy::Process; - LOG_WARN("network", "AntiDOS: Account {}, IP: {}, Ping: {}, Character: {}, flooding packet (opc: {} (0x{:X}), count: {})", - Session->GetAccountId(), Session->GetRemoteAddress(), Session->GetLatency(), Session->GetPlayerName(), - opcodeTable[static_cast(p.GetOpcode())]->Name, p.GetOpcode(), packetCounter.amountCounter); - - switch (_policy) + if (WorldSession::DosProtection::Policy(policy->Policy) != WorldSession::DosProtection::Policy::BlockingThrottle) { - case POLICY_LOG: - return true; - case POLICY_KICK: - { - LOG_INFO("network", "AntiDOS: Player {} kicked!", Session->GetPlayerName()); - Session->KickPlayer(); - return false; - } - case POLICY_BAN: - { - uint32 bm = sWorld->getIntConfig(CONFIG_PACKET_SPOOF_BANMODE); - uint32 duration = sWorld->getIntConfig(CONFIG_PACKET_SPOOF_BANDURATION); // in seconds - std::string nameOrIp = ""; - switch (bm) - { - case 0: // Ban account - (void)AccountMgr::GetName(Session->GetAccountId(), nameOrIp); - sBan->BanAccount(nameOrIp, std::to_string(duration), "DOS (Packet Flooding/Spoofing", "Server: AutoDOS"); - break; - case 1: // Ban ip - nameOrIp = Session->GetRemoteAddress(); - sBan->BanIP(nameOrIp, std::to_string(duration), "DOS (Packet Flooding/Spoofing", "Server: AutoDOS"); - break; - } + LOG_WARN("network", "AntiDOS: Account {}, IP: {}, Ping: {}, Character: {}, flooding packet (opc: {} (0x{:X}), count: {})", + Session->GetAccountId(), Session->GetRemoteAddress(), Session->GetLatency(), Session->GetPlayerName(), + opcodeTable[static_cast(p.GetOpcode())]->Name, p.GetOpcode(), packetCounter.amountCounter); + } - LOG_INFO("network", "AntiDOS: Player automatically banned for {} seconds.", duration); - return false; + switch (WorldSession::DosProtection::Policy(policy->Policy)) + { + case WorldSession::DosProtection::Policy::Kick: + { + LOG_INFO("network", "AntiDOS: Player {} kicked!", Session->GetPlayerName()); + Session->KickPlayer(); + break; + } + case WorldSession::DosProtection::Policy::Ban: + { + uint32 bm = sWorld->getIntConfig(CONFIG_PACKET_SPOOF_BANMODE); + uint32 duration = sWorld->getIntConfig(CONFIG_PACKET_SPOOF_BANDURATION); // in seconds + std::string nameOrIp = ""; + switch (bm) + { + case 0: // Ban account + (void)AccountMgr::GetName(Session->GetAccountId(), nameOrIp); + sBan->BanAccount(nameOrIp, std::to_string(duration), "DOS (Packet Flooding/Spoofing", "Server: AutoDOS"); + break; + case 1: // Ban ip + nameOrIp = Session->GetRemoteAddress(); + sBan->BanIP(nameOrIp, std::to_string(duration), "DOS (Packet Flooding/Spoofing", "Server: AutoDOS"); + break; } + + LOG_INFO("network", "AntiDOS: Player automatically banned for {} seconds.", duration); + break; + } + case WorldSession::DosProtection::Policy::DropPacket: + { + LOG_INFO("network", "AntiDOS: Opcode packet {} from player {} will be dropped.", p.GetOpcode(), Session->GetPlayerName()); + break; + } default: // invalid policy - return true; - } -} - -uint32 WorldSession::DosProtection::GetMaxPacketCounterAllowed(uint16 opcode) const -{ - uint32 maxPacketCounterAllowed; - switch (opcode) - { - // CPU usage sending 2000 packets/second on a 3.70 GHz 4 cores on Win x64 - // [% CPU mysqld] [%CPU worldserver RelWithDebInfo] - case CMSG_PLAYER_LOGIN: // 0 0.5 - case CMSG_NAME_QUERY: // 0 1 - case CMSG_PET_NAME_QUERY: // 0 1 - case CMSG_NPC_TEXT_QUERY: // 0 1 - case CMSG_ATTACKSTOP: // 0 1 - case CMSG_QUERY_QUESTS_COMPLETED: // 0 1 - case CMSG_QUERY_TIME: // 0 1 - case CMSG_CORPSE_MAP_POSITION_QUERY: // 0 1 - case CMSG_MOVE_TIME_SKIPPED: // 0 1 - case MSG_QUERY_NEXT_MAIL_TIME: // 0 1 - case CMSG_SET_SHEATHED: // 0 1 - case MSG_RAID_TARGET_UPDATE: // 0 1 - case CMSG_PLAYER_LOGOUT: // 0 1 - case CMSG_LOGOUT_REQUEST: // 0 1 - case CMSG_PET_RENAME: // 0 1 - case CMSG_QUESTGIVER_CANCEL: // 0 1 - case CMSG_QUESTGIVER_REQUEST_REWARD: // 0 1 - case CMSG_COMPLETE_CINEMATIC: // 0 1 - case CMSG_BANKER_ACTIVATE: // 0 1 - case CMSG_BUY_BANK_SLOT: // 0 1 - case CMSG_OPT_OUT_OF_LOOT: // 0 1 - case CMSG_DUEL_ACCEPTED: // 0 1 - case CMSG_DUEL_CANCELLED: // 0 1 - case CMSG_CALENDAR_COMPLAIN: // 0 1 - case CMSG_QUEST_QUERY: // 0 1.5 - case CMSG_ITEM_QUERY_SINGLE: // 0 1.5 - case CMSG_ITEM_NAME_QUERY: // 0 1.5 - case CMSG_GAMEOBJECT_QUERY: // 0 1.5 - case CMSG_CREATURE_QUERY: // 0 1.5 - case CMSG_QUESTGIVER_STATUS_QUERY: // 0 1.5 - case CMSG_GUILD_QUERY: // 0 1.5 - case CMSG_ARENA_TEAM_QUERY: // 0 1.5 - case CMSG_TAXINODE_STATUS_QUERY: // 0 1.5 - case CMSG_TAXIQUERYAVAILABLENODES: // 0 1.5 - case CMSG_QUESTGIVER_QUERY_QUEST: // 0 1.5 - case CMSG_PAGE_TEXT_QUERY: // 0 1.5 - case MSG_QUERY_GUILD_BANK_TEXT: // 0 1.5 - case MSG_CORPSE_QUERY: // 0 1.5 - case MSG_MOVE_SET_FACING: // 0 1.5 - case CMSG_REQUEST_PARTY_MEMBER_STATS: // 0 1.5 - case CMSG_QUESTGIVER_COMPLETE_QUEST: // 0 1.5 - case CMSG_SET_ACTION_BUTTON: // 0 1.5 - case CMSG_RESET_INSTANCES: // 0 1.5 - case CMSG_HEARTH_AND_RESURRECT: // 0 1.5 - case CMSG_TOGGLE_PVP: // 0 1.5 - case CMSG_PET_ABANDON: // 0 1.5 - case CMSG_ACTIVATETAXIEXPRESS: // 0 1.5 - case CMSG_ACTIVATETAXI: // 0 1.5 - case CMSG_SELF_RES: // 0 1.5 - case CMSG_UNLEARN_SKILL: // 0 1.5 - case CMSG_EQUIPMENT_SET_SAVE: // 0 1.5 - case CMSG_DELETEEQUIPMENT_SET: // 0 1.5 - case CMSG_DISMISS_CRITTER: // 0 1.5 - case CMSG_REPOP_REQUEST: // 0 1.5 - case CMSG_GROUP_INVITE: // 0 1.5 - case CMSG_GROUP_DECLINE: // 0 1.5 - case CMSG_GROUP_ACCEPT: // 0 1.5 - case CMSG_GROUP_UNINVITE_GUID: // 0 1.5 - case CMSG_GROUP_UNINVITE: // 0 1.5 - case CMSG_GROUP_DISBAND: // 0 1.5 - case CMSG_BATTLEMASTER_JOIN_ARENA: // 0 1.5 - case CMSG_LEAVE_BATTLEFIELD: // 0 1.5 - case MSG_GUILD_BANK_LOG_QUERY: // 0 2 - case CMSG_LOGOUT_CANCEL: // 0 2 - case CMSG_REALM_SPLIT: // 0 2 - case CMSG_ALTER_APPEARANCE: // 0 2 - case CMSG_QUEST_CONFIRM_ACCEPT: // 0 2 - case MSG_GUILD_EVENT_LOG_QUERY: // 0 2.5 - case CMSG_READY_FOR_ACCOUNT_DATA_TIMES: // 0 2.5 - case CMSG_QUESTGIVER_STATUS_MULTIPLE_QUERY: // 0 2.5 - case CMSG_BEGIN_TRADE: // 0 2.5 - case CMSG_INITIATE_TRADE: // 0 3 - case CMSG_MESSAGECHAT: // 0 3.5 - case CMSG_INSPECT: // 0 3.5 - case CMSG_AREA_SPIRIT_HEALER_QUERY: // not profiled - case CMSG_STANDSTATECHANGE: // not profiled - case MSG_RANDOM_ROLL: // not profiled - case CMSG_TIME_SYNC_RESP: // not profiled - case CMSG_TRAINER_BUY_SPELL: // not profiled - case CMSG_FORCE_SWIM_SPEED_CHANGE_ACK: // not profiled - case CMSG_FORCE_SWIM_BACK_SPEED_CHANGE_ACK: // not profiled - case CMSG_FORCE_RUN_SPEED_CHANGE_ACK: // not profiled - case CMSG_FORCE_RUN_BACK_SPEED_CHANGE_ACK: // not profiled - case CMSG_FORCE_FLIGHT_SPEED_CHANGE_ACK: // not profiled - case CMSG_FORCE_FLIGHT_BACK_SPEED_CHANGE_ACK: // not profiled - case CMSG_FORCE_WALK_SPEED_CHANGE_ACK: // not profiled - case CMSG_FORCE_TURN_RATE_CHANGE_ACK: // not profiled - case CMSG_FORCE_PITCH_RATE_CHANGE_ACK: // not profiled - { - // "0" is a magic number meaning there's no limit for the opcode. - // All the opcodes above must cause little CPU usage and no sync/async database queries at all - maxPacketCounterAllowed = 0; - break; - } - - case CMSG_QUESTGIVER_ACCEPT_QUEST: // 0 4 - case CMSG_QUESTLOG_REMOVE_QUEST: // 0 4 - case CMSG_QUESTGIVER_CHOOSE_REWARD: // 0 4 - case CMSG_CONTACT_LIST: // 0 5 - case CMSG_LEARN_PREVIEW_TALENTS: // 0 6 - case CMSG_AUTOBANK_ITEM: // 0 6 - case CMSG_AUTOSTORE_BANK_ITEM: // 0 6 - case CMSG_WHO: // 0 7 - case CMSG_PLAYER_VEHICLE_ENTER: // 0 8 - case CMSG_LEARN_PREVIEW_TALENTS_PET: // not profiled - case MSG_MOVE_HEARTBEAT: - { - maxPacketCounterAllowed = 200; - break; - } - - case CMSG_GUILD_SET_PUBLIC_NOTE: // 1 2 1 async db query - case CMSG_GUILD_SET_OFFICER_NOTE: // 1 2 1 async db query - case CMSG_SET_CONTACT_NOTES: // 1 2.5 1 async db query - case CMSG_CALENDAR_GET_CALENDAR: // 0 1.5 medium upload bandwidth usage - case CMSG_GUILD_BANK_QUERY_TAB: // 0 3.5 medium upload bandwidth usage - case CMSG_QUERY_INSPECT_ACHIEVEMENTS: // 0 13 high upload bandwidth usage - case CMSG_GAMEOBJ_REPORT_USE: // not profiled - case CMSG_GAMEOBJ_USE: // not profiled - case MSG_PETITION_DECLINE: // not profiled - { - maxPacketCounterAllowed = 50; - break; - } - - case CMSG_QUEST_POI_QUERY: // 0 25 very high upload bandwidth usage - { - maxPacketCounterAllowed = MAX_QUEST_LOG_SIZE; - break; - } - - case CMSG_GM_REPORT_LAG: // 1 3 1 async db query - case CMSG_SPELLCLICK: // not profiled - case CMSG_REMOVE_GLYPH: // not profiled - case CMSG_DISMISS_CONTROLLED_VEHICLE: // not profiled - { - maxPacketCounterAllowed = 20; - break; - } - - case CMSG_PETITION_SIGN: // 9 4 2 sync 1 async db queries - case CMSG_TURN_IN_PETITION: // 8 5.5 2 sync db query - case CMSG_GROUP_CHANGE_SUB_GROUP: // 6 5 1 sync 1 async db queries - case CMSG_PETITION_QUERY: // 4 3.5 1 sync db query - case CMSG_CHAR_RACE_CHANGE: // 5 4 1 sync db query - case CMSG_CHAR_CUSTOMIZE: // 5 5 1 sync db query - case CMSG_CHAR_FACTION_CHANGE: // 5 5 1 sync db query - case CMSG_CHAR_DELETE: // 4 4 1 sync db query - case CMSG_DEL_FRIEND: // 7 5 1 async db query - case CMSG_ADD_FRIEND: // 6 4 1 async db query - case CMSG_CHAR_RENAME: // 5 3 1 async db query - case CMSG_GMSURVEY_SUBMIT: // 2 3 1 async db query - case CMSG_BUG: // 1 1 1 async db query - case CMSG_GROUP_SET_LEADER: // 1 2 1 async db query - case CMSG_GROUP_RAID_CONVERT: // 1 5 1 async db query - case CMSG_GROUP_ASSISTANT_LEADER: // 1 2 1 async db query - case CMSG_PETITION_BUY: // not profiled 1 sync 1 async db queries - case CMSG_CHANGE_SEATS_ON_CONTROLLED_VEHICLE: // not profiled - case CMSG_REQUEST_VEHICLE_PREV_SEAT: // not profiled - case CMSG_REQUEST_VEHICLE_NEXT_SEAT: // not profiled - case CMSG_REQUEST_VEHICLE_SWITCH_SEAT: // not profiled - case CMSG_REQUEST_VEHICLE_EXIT: // not profiled - case CMSG_CONTROLLER_EJECT_PASSENGER: // not profiled - case CMSG_ITEM_REFUND: // not profiled - case CMSG_SOCKET_GEMS: // not profiled - case CMSG_WRAP_ITEM: // not profiled - case CMSG_REPORT_PVP_AFK: // not profiled - case CMSG_AUCTION_LIST_ITEMS: // not profiled - case CMSG_AUCTION_LIST_BIDDER_ITEMS: // not profiled - case CMSG_AUCTION_LIST_OWNER_ITEMS: // not profiled - { - maxPacketCounterAllowed = 10; - break; - } - - case CMSG_CHAR_CREATE: // 7 5 3 async db queries - case CMSG_CHAR_ENUM: // 22 3 2 async db queries - case CMSG_GMTICKET_CREATE: // 1 25 1 async db query - case CMSG_GMTICKET_UPDATETEXT: // 0 15 1 async db query - case CMSG_GMTICKET_DELETETICKET: // 1 25 1 async db query - case CMSG_GMRESPONSE_RESOLVE: // 1 25 1 async db query - case CMSG_CALENDAR_ADD_EVENT: // 21 10 2 async db query - case CMSG_CALENDAR_UPDATE_EVENT: // not profiled - case CMSG_CALENDAR_REMOVE_EVENT: // not profiled - case CMSG_CALENDAR_COPY_EVENT: // not profiled - case CMSG_CALENDAR_EVENT_INVITE: // not profiled - case CMSG_CALENDAR_EVENT_SIGNUP: // not profiled - case CMSG_CALENDAR_EVENT_RSVP: // not profiled - case CMSG_CALENDAR_EVENT_REMOVE_INVITE: // not profiled - case CMSG_CALENDAR_EVENT_MODERATOR_STATUS: // not profiled - case CMSG_ARENA_TEAM_INVITE: // not profiled - case CMSG_ARENA_TEAM_ACCEPT: // not profiled - case CMSG_ARENA_TEAM_DECLINE: // not profiled - case CMSG_ARENA_TEAM_LEAVE: // not profiled - case CMSG_ARENA_TEAM_DISBAND: // not profiled - case CMSG_ARENA_TEAM_REMOVE: // not profiled - case CMSG_ARENA_TEAM_LEADER: // not profiled - case CMSG_LOOT_METHOD: // not profiled - case CMSG_GUILD_INVITE: // not profiled - case CMSG_GUILD_ACCEPT: // not profiled - case CMSG_GUILD_DECLINE: // not profiled - case CMSG_GUILD_LEAVE: // not profiled - case CMSG_GUILD_DISBAND: // not profiled - case CMSG_GUILD_LEADER: // not profiled - case CMSG_GUILD_MOTD: // not profiled - case CMSG_GUILD_RANK: // not profiled - case CMSG_GUILD_ADD_RANK: // not profiled - case CMSG_GUILD_DEL_RANK: // not profiled - case CMSG_GUILD_INFO_TEXT: // not profiled - case CMSG_GUILD_BANK_DEPOSIT_MONEY: // not profiled - case CMSG_GUILD_BANK_WITHDRAW_MONEY: // not profiled - case CMSG_GUILD_BANK_BUY_TAB: // not profiled - case CMSG_GUILD_BANK_UPDATE_TAB: // not profiled - case CMSG_SET_GUILD_BANK_TEXT: // not profiled - case MSG_SAVE_GUILD_EMBLEM: // not profiled - case MSG_PETITION_RENAME: // not profiled - case MSG_TALENT_WIPE_CONFIRM: // not profiled - case MSG_SET_DUNGEON_DIFFICULTY: // not profiled - case MSG_SET_RAID_DIFFICULTY: // not profiled - case MSG_PARTY_ASSIGNMENT: // not profiled - case MSG_RAID_READY_CHECK: // not profiled - { - maxPacketCounterAllowed = 3; - break; - } - - case CMSG_ITEM_REFUND_INFO: // not profiled - { - maxPacketCounterAllowed = PLAYER_SLOTS_COUNT; - break; - } - - default: - { - maxPacketCounterAllowed = 100; - break; - } + break; } - return maxPacketCounterAllowed; + return WorldSession::DosProtection::Policy(policy->Policy); } WorldSession::DosProtection::DosProtection(WorldSession* s) : - Session(s), _policy((Policy)sWorld->getIntConfig(CONFIG_PACKET_SPOOF_POLICY)) { } + Session(s) { } void WorldSession::ResetTimeSync() { diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h index e2b73bef7..0816b734e 100644 --- a/src/server/game/Server/WorldSession.h +++ b/src/server/game/Server/WorldSession.h @@ -341,7 +341,7 @@ protected: struct PacketCounter { time_t lastReceiveTime; - uint32 amountCounter; + uint16 amountCounter; }; /// Player session in the World @@ -1134,22 +1134,21 @@ protected: { friend class World; public: - DosProtection(WorldSession* s); - bool EvaluateOpcode(WorldPacket& p, time_t time) const; - protected: - enum Policy + enum class Policy { - POLICY_LOG, - POLICY_KICK, - POLICY_BAN + Process, + Kick, + Ban, + Log, + BlockingThrottle, + DropPacket }; - uint32 GetMaxPacketCounterAllowed(uint16 opcode) const; - + DosProtection(WorldSession* s); + Policy EvaluateOpcode(WorldPacket const& p, time_t const time) const; + protected: WorldSession* Session; - private: - Policy _policy; typedef std::unordered_map PacketThrottlingMap; // mark this member as "mutable" so it can be modified even in const functions mutable PacketThrottlingMap _PacketThrottlingMap; diff --git a/src/server/game/Spells/SpellInfoCorrections.cpp b/src/server/game/Spells/SpellInfoCorrections.cpp index dc2ecf080..35092327e 100644 --- a/src/server/game/Spells/SpellInfoCorrections.cpp +++ b/src/server/game/Spells/SpellInfoCorrections.cpp @@ -4884,6 +4884,13 @@ void SpellMgr::LoadSpellInfoCorrections() spellInfo->Effects[EFFECT_1].RealPointsPerLevel = 0.25; }); + // Smash + // Dark Smash + ApplySpellFix({ 42669, 59706, 42723, 59709 }, [](SpellInfo* spellInfo) + { + spellInfo->AttributesEx2 &= ~SPELL_ATTR2_IGNORE_LINE_OF_SIGHT; + }); + for (uint32 i = 0; i < GetSpellInfoStoreSize(); ++i) { SpellInfo* spellInfo = mSpellInfoMap[i]; diff --git a/src/server/game/World/IWorld.h b/src/server/game/World/IWorld.h index f8359ce7a..40cc8a7c5 100644 --- a/src/server/game/World/IWorld.h +++ b/src/server/game/World/IWorld.h @@ -367,7 +367,6 @@ enum WorldIntConfigs CONFIG_WINTERGRASP_BATTLETIME, CONFIG_WINTERGRASP_NOBATTLETIME, CONFIG_WINTERGRASP_RESTART_AFTER_CRASH, - CONFIG_PACKET_SPOOF_POLICY, CONFIG_PACKET_SPOOF_BANMODE, CONFIG_PACKET_SPOOF_BANDURATION, CONFIG_WARDEN_CLIENT_RESPONSE_DELAY, diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index b25d733b5..62482da15 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -91,6 +91,7 @@ #include "WaypointMovementGenerator.h" #include "WeatherMgr.h" #include "WhoListCacheMgr.h" +#include "WorldGlobals.h" #include "WorldPacket.h" #include "WorldSession.h" #include "WorldSessionMgr.h" @@ -1197,7 +1198,6 @@ void World::LoadConfigSettings(bool reload) _bool_configs[CONFIG_SET_ALL_CREATURES_WITH_WAYPOINT_MOVEMENT_ACTIVE] = sConfigMgr->GetOption("SetAllCreaturesWithWaypointMovementActive", false); // packet spoof punishment - _int_configs[CONFIG_PACKET_SPOOF_POLICY] = sConfigMgr->GetOption("PacketSpoof.Policy", (uint32)WorldSession::DosProtection::POLICY_KICK); _int_configs[CONFIG_PACKET_SPOOF_BANMODE] = sConfigMgr->GetOption("PacketSpoof.BanMode", (uint32)0); if (_int_configs[CONFIG_PACKET_SPOOF_BANMODE] > 1) _int_configs[CONFIG_PACKET_SPOOF_BANMODE] = (uint32)0; @@ -1953,6 +1953,9 @@ void World::SetInitialWorldSettings() LOG_INFO("server.loading", "Load Channels..."); ChannelMgr::LoadChannels(); + LOG_INFO("server.loading", "Loading AntiDos opcode policies"); + sWorldGlobals->LoadAntiDosOpcodePolicies(); + sScriptMgr->OnBeforeWorldInitialized(); if (sWorld->getBoolConfig(CONFIG_PRELOAD_ALL_NON_INSTANCED_MAP_GRIDS)) diff --git a/src/server/scripts/Commands/cs_reload.cpp b/src/server/scripts/Commands/cs_reload.cpp index 62bfe7712..45434d502 100644 --- a/src/server/scripts/Commands/cs_reload.cpp +++ b/src/server/scripts/Commands/cs_reload.cpp @@ -47,6 +47,7 @@ EndScriptData */ #include "Tokenize.h" #include "WardenCheckMgr.h" #include "WaypointMgr.h" +#include "WorldGlobals.h" using namespace Acore::ChatCommands; @@ -73,6 +74,7 @@ public: }; static ChatCommandTable reloadCommandTable = { + { "antidos_opcode_policies", HandleReloadAntiDosOpcodePoliciesCommand, SEC_ADMINISTRATOR, Console::Yes }, { "auctions", HandleReloadAuctionsCommand, SEC_ADMINISTRATOR, Console::Yes }, { "dungeon_access_template", HandleReloadDungeonAccessCommand, SEC_ADMINISTRATOR, Console::Yes }, { "dungeon_access_requirements", HandleReloadDungeonAccessCommand, SEC_ADMINISTRATOR, Console::Yes }, @@ -1199,6 +1201,14 @@ public: return true; } + static bool HandleReloadAntiDosOpcodePoliciesCommand(ChatHandler* handler) + { + LOG_INFO("server.loading", "Reloading AntiDos opcode policies..."); + sWorldGlobals->LoadAntiDosOpcodePolicies(); + handler->SendGlobalGMSysMessage("AntiDos opcode policies reloaded."); + return true; + } + static bool HandleReloadAuctionsCommand(ChatHandler* handler) { ///- Reload dynamic data tables from the database diff --git a/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_eredar_twins.cpp b/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_eredar_twins.cpp index a47d16bda..75de3d41c 100644 --- a/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_eredar_twins.cpp +++ b/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_eredar_twins.cpp @@ -377,6 +377,35 @@ class spell_eredar_twins_blaze : public SpellScript } }; +class spell_eredar_twins_handle_touch_periodic : public AuraScript +{ + PrepareAuraScript(spell_eredar_twins_handle_touch_periodic); + +public: + spell_eredar_twins_handle_touch_periodic(uint32 touchSpell, uint8 effIndex, uint8 aura) : AuraScript(), _touchSpell(touchSpell), _effectIndex(effIndex), _aura(aura) {} + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ _touchSpell }); + } + + void OnPeriodic(AuraEffect const* /*aurEff*/) + { + if (Unit* owner = GetOwner()->ToUnit()) + owner->CastSpell(owner, _touchSpell, true); + } + + void Register() override + { + OnEffectPeriodic += AuraEffectPeriodicFn(spell_eredar_twins_handle_touch_periodic::OnPeriodic, _effectIndex, _aura); + } + +private: + uint32 _touchSpell; + uint8 _effectIndex; + uint8 _aura; +}; + class at_sunwell_eredar_twins : public OnlyOnceAreaTriggerScript { public: @@ -404,5 +433,8 @@ void AddSC_boss_eredar_twins() RegisterSpellScriptWithArgs(spell_eredar_twins_apply_touch, "spell_eredar_twins_apply_flame_touched", SPELL_FLAME_TOUCHED); RegisterSpellScript(spell_eredar_twins_handle_touch); RegisterSpellScript(spell_eredar_twins_blaze); + RegisterSpellScriptWithArgs(spell_eredar_twins_handle_touch_periodic, "spell_eredar_twins_handle_dark_touched_periodic", SPELL_DARK_TOUCHED, EFFECT_1, SPELL_AURA_PERIODIC_DAMAGE); + RegisterSpellScriptWithArgs(spell_eredar_twins_handle_touch_periodic, "spell_eredar_twins_handle_flame_touched_periodic", SPELL_FLAME_TOUCHED, EFFECT_2, SPELL_AURA_PERIODIC_TRIGGER_SPELL); + RegisterSpellScriptWithArgs(spell_eredar_twins_handle_touch_periodic, "spell_eredar_twins_handle_flame_touched_flame_sear", SPELL_FLAME_TOUCHED, EFFECT_1, SPELL_AURA_PERIODIC_DAMAGE); new at_sunwell_eredar_twins(); } diff --git a/src/server/scripts/EasternKingdoms/ZulAman/zulaman.cpp b/src/server/scripts/EasternKingdoms/ZulAman/zulaman.cpp index 7e1f7465c..aaaaf409c 100644 --- a/src/server/scripts/EasternKingdoms/ZulAman/zulaman.cpp +++ b/src/server/scripts/EasternKingdoms/ZulAman/zulaman.cpp @@ -710,7 +710,6 @@ private: enum AmanishiScout { NPC_WORLD_TRIGGER = 22515, - POINT_DRUM = 0, SAY_AGGRO = 0, SPELL_ALERT_DRUMS = 42177, SPELL_MULTI_SHOT = 43205, @@ -737,6 +736,8 @@ struct npc_amanishi_scout : public ScriptedAI { scheduler.CancelAll(); me->SetCombatMovement(false); + me->SetReactState(REACT_AGGRESSIVE); + _drumGUID.Clear(); } void JustEngagedWith(Unit* /*who*/) override @@ -748,30 +749,38 @@ struct npc_amanishi_scout : public ScriptedAI GetCreatureListWithEntryInGrid(triggers, me, NPC_WORLD_TRIGGER, 50.0f); triggers.remove_if([](Creature* trigger) {return !IsDrum(trigger);}); triggers.sort(Acore::ObjectDistanceOrderPred(me)); - if (!triggers.empty()) - { - me->ClearTarget(); - Creature* closestDrum = triggers.front(); - me->GetMotionMaster()->MovePoint(POINT_DRUM, closestDrum->GetPositionX(), closestDrum->GetPositionY(), closestDrum->GetPositionZ()); - } - else + if (triggers.empty()) ScheduleCombat(); - } - - void MovementInform(uint32 type, uint32 id) override - { - if (type == POINT_MOTION_TYPE && id == POINT_DRUM) + Creature* closestDrum = triggers.front(); + me->GetMotionMaster()->MoveFollow(closestDrum, 0.0f, 0.0f); + _drumGUID = closestDrum->GetGUID(); + me->ClearTarget(); + me->SetReactState(REACT_PASSIVE); + scheduler.Schedule(1s, [this](TaskContext context) { - DoCastSelf(SPELL_ALERT_DRUMS); - scheduler.Schedule(5s, [this](TaskContext /*context*/) - { - ScheduleCombat(); - }); - } + if (_drumGUID) + if (Creature* drum = ObjectAccessor::GetCreature(*me, _drumGUID)) + { + if (me->IsWithinRange(drum, INTERACTION_DISTANCE)) + { + me->SetFacingToObject(drum); + DoCastSelf(SPELL_ALERT_DRUMS); + scheduler.Schedule(5s, [this](TaskContext /*context*/) + { + ScheduleCombat(); + }); + return; + } + context.Repeat(1s); + return; + } + ScheduleCombat(); + }); } void ScheduleCombat() { + me->SetReactState(REACT_AGGRESSIVE); me->SetCombatMovement(true); if (Unit* victim = me->GetVictim()) me->GetMotionMaster()->MoveChase(victim); @@ -790,11 +799,13 @@ struct npc_amanishi_scout : public ScriptedAI { scheduler.Update(diff); - if (!UpdateVictim()) + if (!me->IsCombatMovementAllowed() || !UpdateVictim()) return; DoMeleeAttackIfReady(); } +private: + ObjectGuid _drumGUID; }; enum SpellAlertDrums diff --git a/src/server/scripts/Northrend/FrozenHalls/ForgeOfSouls/instance_forge_of_souls.cpp b/src/server/scripts/Northrend/FrozenHalls/ForgeOfSouls/instance_forge_of_souls.cpp index c39c3a5e6..812e2f3e5 100644 --- a/src/server/scripts/Northrend/FrozenHalls/ForgeOfSouls/instance_forge_of_souls.cpp +++ b/src/server/scripts/Northrend/FrozenHalls/ForgeOfSouls/instance_forge_of_souls.cpp @@ -46,7 +46,6 @@ public: } uint32 m_auiEncounter[MAX_ENCOUNTER]; - TeamId teamIdInInstance; std::string str_data; ObjectGuid NPC_BronjahmGUID; ObjectGuid NPC_DevourerGUID; @@ -59,7 +58,6 @@ public: void Initialize() override { memset(&m_auiEncounter, 0, sizeof(m_auiEncounter)); - teamIdInInstance = TEAM_NEUTRAL; } bool IsEncounterInProgress() const override @@ -72,21 +70,7 @@ public: void OnPlayerEnter(Player* player) override { - if (teamIdInInstance == TEAM_NEUTRAL) - { - if (Group* group = player->GetGroup()) - { - if (Player* gLeader = ObjectAccessor::FindPlayer(group->GetLeaderGUID())) - teamIdInInstance = Player::TeamIdForRace(gLeader->getRace()); - else - teamIdInInstance = player->GetTeamId(); - } - else - teamIdInInstance = player->GetTeamId(); - } - - if (sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GROUP)) - player->SetFaction((teamIdInInstance == TEAM_HORDE) ? 1610 : 1); + InstanceScript::OnPlayerEnter(player); // this will happen only after crash and loading the instance from db if (m_auiEncounter[0] == DONE && m_auiEncounter[1] == DONE && (!NPC_LeaderSecondGUID || !instance->GetCreature(NPC_LeaderSecondGUID))) @@ -98,24 +82,6 @@ public: void OnCreatureCreate(Creature* creature) override { - if (teamIdInInstance == TEAM_NEUTRAL) - { - Map::PlayerList const& players = instance->GetPlayers(); - if (!players.IsEmpty()) - if (Player* player = players.begin()->GetSource()) - { - if (Group* group = player->GetGroup()) - { - if (Player* gLeader = ObjectAccessor::FindPlayer(group->GetLeaderGUID())) - teamIdInInstance = Player::TeamIdForRace(gLeader->getRace()); - else - teamIdInInstance = player->GetTeamId(); - } - else - teamIdInInstance = player->GetTeamId(); - } - } - switch (creature->GetEntry()) { case NPC_BRONJAHM: @@ -125,7 +91,7 @@ public: NPC_DevourerGUID = creature->GetGUID(); break; case NPC_SYLVANAS_PART1: - if (teamIdInInstance == TEAM_ALLIANCE) + if (GetTeamIdInInstance() == TEAM_ALLIANCE) creature->UpdateEntry(NPC_JAINA_PART1); NPC_LeaderFirstGUID = creature->GetGUID(); @@ -133,12 +99,12 @@ public: creature->SetVisible(false); break; case NPC_SYLVANAS_PART2: - if (teamIdInInstance == TEAM_ALLIANCE) + if (GetTeamIdInInstance() == TEAM_ALLIANCE) creature->UpdateEntry(NPC_JAINA_PART2); NPC_LeaderSecondGUID = creature->GetGUID(); break; case NPC_LORALEN: - if (teamIdInInstance == TEAM_ALLIANCE) + if (GetTeamIdInInstance() == TEAM_ALLIANCE) creature->UpdateEntry(NPC_ELANDRA); if (!NPC_GuardFirstGUID) { @@ -148,7 +114,7 @@ public: } break; case NPC_KALIRA: - if (teamIdInInstance == TEAM_ALLIANCE) + if (GetTeamIdInInstance() == TEAM_ALLIANCE) creature->UpdateEntry(NPC_KORELN); if (!NPC_GuardSecondGUID) { @@ -170,9 +136,12 @@ public: leader->GetMotionMaster()->MovePoint(1, boss->GetPositionX() + 10.0f * cos(angle), boss->GetPositionY() + 10.0f * std::sin(angle), boss->GetPositionZ()); } - for (int8 i = 0; outroPositions[i].entry[teamIdInInstance] != 0; ++i) - if (Creature* summon = instance->SummonCreature(outroPositions[i].entry[teamIdInInstance], outroPositions[i].startPosition)) - summon->GetMotionMaster()->MovePath(outroPositions[i].pathId, false); + if (GetTeamIdInInstance() < TEAM_NEUTRAL) // Shouldn't happen, but just in case. + { + for (int8 i = 0; outroPositions[i].entry[GetTeamIdInInstance()] != 0; ++i) + if (Creature* summon = instance->SummonCreature(outroPositions[i].entry[GetTeamIdInInstance()], outroPositions[i].startPosition)) + summon->GetMotionMaster()->MovePath(outroPositions[i].pathId, false); + } } void SetData(uint32 type, uint32 data) override diff --git a/src/server/scripts/Spells/spell_hunter.cpp b/src/server/scripts/Spells/spell_hunter.cpp index 637b355ab..ba7a82768 100644 --- a/src/server/scripts/Spells/spell_hunter.cpp +++ b/src/server/scripts/Spells/spell_hunter.cpp @@ -887,8 +887,11 @@ class spell_hun_misdirection : public AuraScript GetTarget()->ResetRedirectThreat(); } - bool CheckProc(ProcEventInfo& /*eventInfo*/) + bool CheckProc(ProcEventInfo& eventInfo) { + // Do not trigger from Mend Pet + if (eventInfo.GetProcSpell() && (eventInfo.GetProcSpell()->GetSpellInfo()->SpellFamilyFlags[0] & 0x800000)) + return false; return GetTarget()->GetRedirectThreatTarget(); }