From 059db728f5dfc96778248cdd5ae659aa0bc1fd42 Mon Sep 17 00:00:00 2001 From: Kylexy Date: Tue, 28 Jul 2026 13:14:32 +0200 Subject: [PATCH] Fix current AzerothCore compatibility --- README.md | 8 +- .../game/Entities/Player/PlayerUpdates.cpp | 146 ++++++++++++------ sql/world/EntryChecker_RUN_FIRST.sql | 4 +- sql/world/FactionFree.sql | 6 +- 4 files changed, 108 insertions(+), 56 deletions(-) diff --git a/README.md b/README.md index 7fc88fc..3680ebe 100644 --- a/README.md +++ b/README.md @@ -25,9 +25,10 @@ This is not a module that you need to place within that directory, but rather a 6. Add the Patch-F.MPQ to your client data directory - **NOTE**: If you already have a Patch-F.MPQ file in use, simply change the "F" to an unused letter. I chose F as all of these changes are tied to Faction. 7. Log into MySQL and run the attached .SQL files against the acore_world database - - First, run the EntryChecker.sql file to determine if any of the id, entry, or guid are currently in use + - First, run the EntryChecker_RUN_FIRST.sql file to determine if any of the id, entry, or guid values are currently in use - If things are found, please modify the FactionFree.sql to use values that are not in use - After ensuring no values are already in use, or after changing to ones that are free, run the FactionFree.sql against acore_world + - Make sure your AzerothCore database is fully updated before importing the files; the SQL targets the schema shipped with the current `main` branch 8. Navigate to the directory shown under the cpp directory in this repo, copy the EXISTING PlayerUpdates.cpp file to something like PlayerUpdates.cpp.bkup to keep the original file incase you need it in the future. Then copy the PlayerUpdates.cpp file from the repo into that location. 9. Recompile and Remake the server as normal and enjoy! @@ -96,3 +97,8 @@ v1.3.5: December 12, 2024 v1.3.6: February 15, 2025 - Updated PlayerUpdates.cpp to work with current Azerothcore main branch after changes to .cpp - Added faction 1637 to FactionFree.sql to correct faction Horde NPCs attacking Alliance players + +v1.3.7: July 28, 2026 +- Updated PlayerUpdates.cpp to match the current AzerothCore main branch while preserving the faction-free city behavior +- Updated creature_template inserts for the current database schema +- Made EntryChecker_RUN_FIRST.sql independent of the creature identifier column name diff --git a/cpp/src/server/game/Entities/Player/PlayerUpdates.cpp b/cpp/src/server/game/Entities/Player/PlayerUpdates.cpp index 5ba53c5..ae3e2a0 100644 --- a/cpp/src/server/game/Entities/Player/PlayerUpdates.cpp +++ b/cpp/src/server/game/Entities/Player/PlayerUpdates.cpp @@ -1,14 +1,14 @@ /* * 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 free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 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 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along @@ -68,14 +68,8 @@ void Player::Update(uint32 p_time) m_nextMailDelivereTime = time_t(0); } - // Update cinematic location, if 500ms have passed and we're doing a - // cinematic now. - _cinematicMgr->m_cinematicDiff += p_time; - if (_cinematicMgr->m_cinematicCamera && _cinematicMgr->m_activeCinematicCameraId && GetMSTimeDiffToNow(_cinematicMgr->m_lastCinematicCheck) > CINEMATIC_UPDATEDIFF) - { - _cinematicMgr->m_lastCinematicCheck = getMSTime(); - _cinematicMgr->UpdateCinematicLocation(p_time); - } + // Update cinematic camera (if needed) + _cinematicMgr.UpdateCinematic(p_time); // used to implement delayed far teleports SetMustDelayTeleport(true); @@ -204,6 +198,9 @@ void Player::Update(uint32 p_time) // do attack AttackerStateUpdate(victim, BASE_ATTACK); resetAttackTimer(BASE_ATTACK); + + // Blizzlike: Reset ranged swing timer when performing melee attack + resetAttackTimer(RANGED_ATTACK); } } @@ -223,6 +220,9 @@ void Player::Update(uint32 p_time) // do attack AttackerStateUpdate(victim, OFF_ATTACK); resetAttackTimer(OFF_ATTACK); + + // Blizzlike: Reset ranged swing timer when performing melee attack + resetAttackTimer(RANGED_ATTACK); } } @@ -332,6 +332,8 @@ void Player::Update(uint32 p_time) } } + UpdateAdditionalSaves(p_time); + // Handle Water/drowning HandleDrowning(p_time); @@ -396,13 +398,11 @@ void Player::Update(uint32 p_time) // != GetCharmGUID()))) RemovePet(pet, PET_SAVE_NOT_IN_SLOT, true); - // pussywizard: if (m_hostileReferenceCheckTimer <= p_time) { m_hostileReferenceCheckTimer = 15000; if (!GetMap()->IsDungeon()) - getHostileRefMgr().deleteReferencesOutOfRange( - GetVisibilityRange()); + GetCombatManager().EndCombatBeyondRange(GetVisibilityRange(), true); } else m_hostileReferenceCheckTimer -= p_time; @@ -701,7 +701,7 @@ void Player::UpdateRating(CombatRating cr) void Player::UpdateAllRatings() { - for (int cr = 0; cr < MAX_COMBAT_RATING; ++cr) + for (uint8 cr = 0; cr < MAX_COMBAT_RATING; ++cr) UpdateRating(CombatRating(cr)); } @@ -941,6 +941,11 @@ bool Player::UpdateSkillPro(uint16 SkillId, int32 Chance, uint32 step) if (!MaxValue || !SkillValue || SkillValue >= MaxValue) return false; + // Trial account trade-skill cap (0 disables the cap) + if (uint32 trialSkillCap = sWorld->getIntConfig(CONFIG_TRIAL_TRADE_SKILL_CAP)) + if (GetSession()->IsTrialAccount() && SkillValue >= trialSkillCap) + return false; + int32 Roll = irand(1, 1000); if (Roll <= Chance) @@ -1171,9 +1176,6 @@ bool Player::UpdatePosition(float x, float y, float z, float orientation, if (GetGroup()) SetGroupUpdateFlag(GROUP_UPDATE_FLAG_POSITION); - if (GetTrader() && !IsWithinDistInMap(GetTrader(), INTERACTION_DISTANCE)) - GetSession()->SendCancelTrade(TRADE_STATUS_TRADE_CANCELED); - CheckAreaExploreAndOutdoor(); return true; @@ -1235,7 +1237,8 @@ void Player::UpdateArea(uint32 newArea) { SetByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_SANCTUARY); pvpInfo.IsInNoPvPArea = true; - CombatStopWithPets(); + if (!duel && GetCombatManager().HasPvPCombat()) + CombatStopWithPets(); } else RemoveByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_SANCTUARY); @@ -1289,13 +1292,9 @@ void Player::UpdateZone(uint32 newZone, uint32 newArea, bool force) return; if (sWorld->getBoolConfig(CONFIG_WEATHER)) - { - if (Weather* weather = WeatherMgr::FindWeather(zone->ID)) - weather->SendWeatherUpdateToPlayer(this); - else if (!WeatherMgr::AddWeather(zone->ID)) - // send fine weather packet to remove old zone's weather - WeatherMgr::SendFineWeatherUpdateToPlayer(this); - } + GetMap()->GetOrGenerateZoneDefaultWeather(newZone); + + GetMap()->SendZoneDynamicInfo(newZone, this); sScriptMgr->OnPlayerUpdateZone(this, newZone, newArea); @@ -1409,7 +1408,7 @@ void Player::UpdateHomebindTime(uint32 time) WorldPacket data(SMSG_RAID_GROUP_ONLY, 4 + 4); data << uint32(0); data << uint32(0); - GetSession()->SendPacket(&data); + SendDirectMessage(&data); } // instance is valid, reset homebind timer m_HomebindTimer = 0; @@ -1432,7 +1431,7 @@ void Player::UpdateHomebindTime(uint32 time) WorldPacket data(SMSG_RAID_GROUP_ONLY, 4 + 4); data << uint32(m_HomebindTimer); data << uint32(1); - GetSession()->SendPacket(&data); + SendDirectMessage(&data); LOG_DEBUG( "maps", "PLAYER: Player '{}' ({}) will be teleported to homebind in 60 " @@ -1447,6 +1446,9 @@ void Player::UpdatePvPState() if (pvpInfo.IsHostile) // in hostile area { + if (IsInFlight() || !m_taxi.empty()) // on taxi or taxi pending resume after login + return; + if (!IsPvP() || pvpInfo.EndTimer != 0) UpdatePvP(true, true); } @@ -1548,6 +1550,16 @@ void Player::UpdatePvP(bool state, bool _override) sScriptMgr->OnPlayerPVPFlagChange(this, state); } +void Player::AtExitCombat() +{ + Unit::AtExitCombat(); + UpdatePotionCooldown(); + + if (IsClass(CLASS_DEATH_KNIGHT, CLASS_CONTEXT_ABILITY)) + for (uint8 i = 0; i < MAX_RUNES; ++i) + SetGracePeriod(i, 0); +} + void Player::UpdatePotionCooldown(Spell* spell) { // no potion used i combat or still in combat @@ -1598,21 +1610,12 @@ void Player::UpdateVisibilityForPlayer(bool mapChange) // After added to map seer must be a player - there is no possibility to // still have different seer (all charm auras must be already removed) if (mapChange && m_seer != this) - { m_seer = this; - } - - Acore::VisibleNotifier notifierNoLarge( - *this, mapChange, - false); // visit only objects which are not large; default distance - Cell::VisitObjects(m_seer, notifierNoLarge, - GetSightRange() + VISIBILITY_INC_FOR_GOBJECTS); - notifierNoLarge.SendToSelf(); - Acore::VisibleNotifier notifierLarge( - *this, mapChange, true); // visit only large objects; maximum distance - Cell::VisitObjects(m_seer, notifierLarge, GetSightRange()); - notifierLarge.SendToSelf(); + Acore::VisibleNotifier notifier(*this, mapChange); + Cell::VisitObjects(GetSightPosition().GetPositionX(), GetSightPosition().GetPositionY(), GetMap(), notifier, GetSightRange()); + Cell::VisitFarVisibleObjects(GetSightPosition().GetPositionX(), GetSightPosition().GetPositionY(), GetMap(), notifier, VISIBILITY_DISTANCE_GIGANTIC); + notifier.SendToSelf(); if (mapChange) m_last_notify_position.Relocate(-5000.0f, -5000.0f, -5000.0f, 0.0f); @@ -1788,7 +1791,7 @@ void Player::UpdateTriggerVisibility() WorldPacket packet; udata.BuildPacket(packet); - GetSession()->SendPacket(&packet); + SendDirectMessage(&packet); } void Player::UpdateForQuestWorldObjects() @@ -1841,7 +1844,7 @@ void Player::UpdateForQuestWorldObjects() WorldPacket packet; udata.BuildPacket(packet); - GetSession()->SendPacket(&packet); + SendDirectMessage(&packet); } void Player::UpdateTitansGrip() @@ -1985,10 +1988,7 @@ void Player::UpdateCharmedAI() Unit* target = GetVictim(); if (target) - { SetInFront(target); - SendMovementFlagUpdate(true); - } if (HasUnitState(UNIT_STATE_CASTING)) return; @@ -2329,9 +2329,9 @@ bool Player::CanExecutePendingSpellCastRequest(SpellInfo const* spellInfo) return true; } -const PendingSpellCastRequest* Player::GetCastRequest(uint32 category) const +PendingSpellCastRequest const* Player::GetCastRequest(uint32 category) const { - for (const PendingSpellCastRequest& request : SpellQueue) + for (PendingSpellCastRequest const& request : SpellQueue) if (request.category == category) return &request; return nullptr; @@ -2405,3 +2405,49 @@ void Player::ProcessSpellQueue() break; } } + +// save only the data flagged by AdditionalSavingAddMask shortly after +// important changes, so a crash loses at most a few seconds of them +void Player::UpdateAdditionalSaves(uint32 p_time) +{ + if (!m_additionalSaveTimer || GetSession()->isLogingOut()) + return; + + if (m_additionalSaveTimer > p_time) + { + m_additionalSaveTimer -= p_time; + return; + } + + uint8 mask = m_additionalSaveMask; + m_additionalSaveTimer = 0; + m_additionalSaveMask = 0; + + CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction(); + + if (mask & ADDITIONAL_SAVING_INVENTORY_AND_GOLD) + SaveInventoryAndGoldToDB(trans); + + if (mask & ADDITIONAL_SAVING_QUEST_STATUS) + { + _SaveQuestStatus(trans); + + // if nothing changed, nothing will happen + _SaveDailyQuestStatus(trans); + _SaveWeeklyQuestStatus(trans); + _SaveSeasonalQuestStatus(trans); + _SaveMonthlyQuestStatus(trans); + } + + if (mask & ADDITIONAL_SAVING_ACHIEVEMENTS) + { + m_achievementMgr->SaveToDB(trans); + + // achievements are often earned together with skill or gold changes + // (professions, riding, wealth), save those too to keep the DB consistent + _SaveSkills(trans); + SaveGoldToDB(trans); + } + + CharacterDatabase.CommitTransaction(trans); +} diff --git a/sql/world/EntryChecker_RUN_FIRST.sql b/sql/world/EntryChecker_RUN_FIRST.sql index fced0a2..03e0231 100644 --- a/sql/world/EntryChecker_RUN_FIRST.sql +++ b/sql/world/EntryChecker_RUN_FIRST.sql @@ -18,7 +18,7 @@ To run this script: /*Check to see if entry 500000 and 500001 are free to add the creature templates for the Teleport Network*/ SELECT `entry`, `name`, `subname`, `faction`, `npcflag` FROM `acore_world`.`creature_template` WHERE `entry` IN (500000, 500001); /*Check to see if GUIDs 500000000-500000019 are free to add the creature objects for the Teleport Network*/ -SELECT `guid`, `id1`, `map`, `zoneId`, `areaId` FROM `acore_world`.`creature` WHERE `guid` BETWEEN 5000000 AND 5000019; +SELECT `guid`, `map`, `zoneId`, `areaId` FROM `acore_world`.`creature` WHERE `guid` BETWEEN 5000000 AND 5000019; /*Check to see if entry 500000, 500001, and 500003 are free to add the broadcast text entries for the Teleport Network*/ SELECT `ID`, `LanguageID`, `MaleText`, `FemaleText` FROM `acore_world`.`broadcast_text` WHERE `ID` IN (500000, 500001, 500003); /*Check to see if entry 500000, 500001, and 500003 are free to add the npc text entries for the Teleport Network*/ @@ -29,4 +29,4 @@ SELECT `entry`, `type`, `displayId`, `name` FROM `acore_world`.`gameobject_templ SELECT `guid`, `id`, `map`, `zoneId`, `areaId` FROM `acore_world`.`gameobject` WHERE `guid` BETWEEN 5000000 AND 5000049; /*AGAIN: Please note that if you have any results back from these queries you will need to modify the FactionFree.sql -to use new and free entry, id, or GUID values.*/ \ No newline at end of file +to use new and free entry, id, or GUID values.*/ diff --git a/sql/world/FactionFree.sql b/sql/world/FactionFree.sql index 8afb917..2e53da4 100644 --- a/sql/world/FactionFree.sql +++ b/sql/world/FactionFree.sql @@ -81,8 +81,8 @@ INSERT INTO `acore_world`.`npc_text` (`ID`, `text0_0`, `text0_1`, `BroadcastText may have fewer cities or flight paths. They will use entry 500000 and 500001. If these are already in use within your environment, you can change those values here.*/ DELETE FROM `acore_world`.`creature_template` WHERE `entry` BETWEEN 500000 AND 500001; -INSERT INTO `acore_world`.`creature_template` (`entry`, `difficulty_entry_1`, `difficulty_entry_2`, `difficulty_entry_3`, `KillCredit1`, `KillCredit2`, `name`, `subname`, `IconName`, `gossip_menu_id`, `minlevel`, `maxlevel`, `exp`, `faction`, `npcflag`, `speed_walk`, `speed_run`, `speed_swim`, `speed_flight`, `detection_range`, `scale`, `rank`, `dmgschool`, `DamageModifier`, `BaseAttackTime`, `RangeAttackTime`, `BaseVariance`, `RangeVariance`, `unit_class`, `unit_flags`, `unit_flags2`, `dynamicflags`, `family`, `trainer_type`, `trainer_spell`, `trainer_class`, `trainer_race`, `type`, `type_flags`, `lootid`, `pickpocketloot`, `skinloot`, `PetSpellDataId`, `VehicleId`, `mingold`, `maxgold`, `AIName`, `MovementType`, `HoverHeight`, `HealthModifier`, `ManaModifier`, `ArmorModifier`, `ExperienceModifier`, `RacialLeader`, `movementId`, `RegenHealth`, `mechanic_immune_mask`, `spell_school_immune_mask`, `flags_extra`, `ScriptName`, `VerifiedBuild`) VALUES (500000, 0, 0, 0, 0, 0, 'Quinley Gearspark', 'Gobl-ome Engieering Co.', NULL, 0, 35, 35, 0, 474, 1, 1, 1.14286, 1, 1, 18, 1, 0, 0, 1, 2000, 2000, 1, 1, 8, 512, 2048, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, '', 0, 1, 1.1, 1, 1, 1, 0, 0, 1, 0, 0, 2, '', 12340); -INSERT INTO `acore_world`.`creature_template` (`entry`, `difficulty_entry_1`, `difficulty_entry_2`, `difficulty_entry_3`, `KillCredit1`, `KillCredit2`, `name`, `subname`, `IconName`, `gossip_menu_id`, `minlevel`, `maxlevel`, `exp`, `faction`, `npcflag`, `speed_walk`, `speed_run`, `speed_swim`, `speed_flight`, `detection_range`, `scale`, `rank`, `dmgschool`, `DamageModifier`, `BaseAttackTime`, `RangeAttackTime`, `BaseVariance`, `RangeVariance`, `unit_class`, `unit_flags`, `unit_flags2`, `dynamicflags`, `family`, `trainer_type`, `trainer_spell`, `trainer_class`, `trainer_race`, `type`, `type_flags`, `lootid`, `pickpocketloot`, `skinloot`, `PetSpellDataId`, `VehicleId`, `mingold`, `maxgold`, `AIName`, `MovementType`, `HoverHeight`, `HealthModifier`, `ManaModifier`, `ArmorModifier`, `ExperienceModifier`, `RacialLeader`, `movementId`, `RegenHealth`, `mechanic_immune_mask`, `spell_school_immune_mask`, `flags_extra`, `ScriptName`, `VerifiedBuild`) VALUES (500001, 0, 0, 0, 0, 0, 'Harleen Gearspark', 'Gnom-lin Engieering Co.', NULL, 0, 35, 35, 0, 474, 1, 1, 1.14286, 1, 1, 18, 1, 0, 0, 1, 2000, 2000, 1, 1, 1, 512, 2048, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, '', 0, 1, 1.1, 1, 1, 1, 0, 0, 1, 0, 0, 2, '', 12340); +INSERT INTO `acore_world`.`creature_template` (`entry`, `difficulty_entry_1`, `difficulty_entry_2`, `difficulty_entry_3`, `KillCredit1`, `KillCredit2`, `name`, `subname`, `IconName`, `gossip_menu_id`, `minlevel`, `maxlevel`, `exp`, `faction`, `npcflag`, `speed_walk`, `speed_run`, `speed_swim`, `speed_flight`, `detection_range`, `rank`, `dmgschool`, `DamageModifier`, `BaseAttackTime`, `RangeAttackTime`, `BaseVariance`, `RangeVariance`, `unit_class`, `unit_flags`, `unit_flags2`, `dynamicflags`, `family`, `type`, `type_flags`, `lootid`, `pickpocketloot`, `skinloot`, `PetSpellDataId`, `VehicleId`, `mingold`, `maxgold`, `AIName`, `MovementType`, `HoverHeight`, `HealthModifier`, `ManaModifier`, `ArmorModifier`, `ExperienceModifier`, `RacialLeader`, `movementId`, `RegenHealth`, `CreatureImmunitiesId`, `flags_extra`, `ScriptName`, `VerifiedBuild`) VALUES (500000, 0, 0, 0, 0, 0, 'Quinley Gearspark', 'Gobl-ome Engieering Co.', NULL, 0, 35, 35, 0, 474, 1, 1, 1.14286, 1, 1, 18, 0, 0, 1, 2000, 2000, 1, 1, 8, 512, 2048, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, '', 0, 1, 1.1, 1, 1, 1, 0, 0, 1, 0, 2, '', 12340); +INSERT INTO `acore_world`.`creature_template` (`entry`, `difficulty_entry_1`, `difficulty_entry_2`, `difficulty_entry_3`, `KillCredit1`, `KillCredit2`, `name`, `subname`, `IconName`, `gossip_menu_id`, `minlevel`, `maxlevel`, `exp`, `faction`, `npcflag`, `speed_walk`, `speed_run`, `speed_swim`, `speed_flight`, `detection_range`, `rank`, `dmgschool`, `DamageModifier`, `BaseAttackTime`, `RangeAttackTime`, `BaseVariance`, `RangeVariance`, `unit_class`, `unit_flags`, `unit_flags2`, `dynamicflags`, `family`, `type`, `type_flags`, `lootid`, `pickpocketloot`, `skinloot`, `PetSpellDataId`, `VehicleId`, `mingold`, `maxgold`, `AIName`, `MovementType`, `HoverHeight`, `HealthModifier`, `ManaModifier`, `ArmorModifier`, `ExperienceModifier`, `RacialLeader`, `movementId`, `RegenHealth`, `CreatureImmunitiesId`, `flags_extra`, `ScriptName`, `VerifiedBuild`) VALUES (500001, 0, 0, 0, 0, 0, 'Harleen Gearspark', 'Gnom-lin Engieering Co.', NULL, 0, 35, 35, 0, 474, 1, 1, 1.14286, 1, 1, 18, 0, 0, 1, 2000, 2000, 1, 1, 1, 512, 2048, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, '', 0, 1, 1.1, 1, 1, 1, 0, 0, 1, 0, 2, '', 12340); /*This will create a series of objects in gameobject_template that were used to add asthetics to the teleport network. They have no purpose other than just to make the area stand out and look better. They will use entry 500000-500004. If these are already in use within your @@ -182,4 +182,4 @@ UPDATE `creature_template` SET faction = 11 WHERE faction IN (53, 56, 84, 1733, UPDATE `creature_template` SET faction = 14 WHERE entry IN (36950,38406,38685,38686,36957,38404,38679,38680,36960,38262,38683,38684,36961,38261,38691,38692,36968,38403,38675,38676,36969,38408,38689,38690,36978,38407,38687,38688,36982,38405,38681,38682,37116,38256,38693,38694,37117,38257,38677,38678); /*This will modify the faction of the two NPCs, Sunreaver and Silver Covenant Agents, required to be killed for the WOTLK "The Battered Hilt" quest chains by the opposing faction*/ -UPDATE `acore_world`.`creature_template` SET `faction` = 7 WHERE `entry` IN (36776, 36774); \ No newline at end of file +UPDATE `acore_world`.`creature_template` SET `faction` = 7 WHERE `entry` IN (36776, 36774);