diff --git a/src/server/database/Database/Implementation/CharacterDatabase.cpp b/src/server/database/Database/Implementation/CharacterDatabase.cpp index bef2555d0d..a60b25ae63 100644 --- a/src/server/database/Database/Implementation/CharacterDatabase.cpp +++ b/src/server/database/Database/Implementation/CharacterDatabase.cpp @@ -278,7 +278,7 @@ void CharacterDatabaseConnection::DoPrepareStatements() // Arena teams PrepareStatement(CHAR_INS_ARENA_TEAM, "INSERT INTO arena_team (arenaTeamId, name, captainGuid, type, rating, backgroundColor, emblemStyle, emblemColor, borderStyle, borderColor) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC); - PrepareStatement(CHAR_INS_ARENA_TEAM_MEMBER, "INSERT INTO arena_team_member (arenaTeamId, guid, personalRating) VALUES (?, ?, ?)", CONNECTION_ASYNC); + PrepareStatement(CHAR_INS_ARENA_TEAM_MEMBER, "INSERT INTO arena_team_member (arenaTeamId, guid, weekGames, weekWins, seasonGames, seasonWins, personalRating) VALUES (?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_ARENA_TEAM, "DELETE FROM arena_team WHERE arenaTeamId = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_ARENA_TEAM_MEMBERS, "DELETE FROM arena_team_member WHERE arenaTeamId = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_UPD_ARENA_TEAM_CAPTAIN, "UPDATE arena_team SET captainGuid = ? WHERE arenaTeamId = ?", CONNECTION_ASYNC); diff --git a/src/server/game/Battlegrounds/ArenaTeam.cpp b/src/server/game/Battlegrounds/ArenaTeam.cpp index adf63a16d4..04ae2488dd 100644 --- a/src/server/game/Battlegrounds/ArenaTeam.cpp +++ b/src/server/game/Battlegrounds/ArenaTeam.cpp @@ -137,8 +137,6 @@ bool ArenaTeam::AddMember(ObjectGuid playerGuid) else if (GetRating() >= 1000) personalRating = 1000; - sScriptMgr->OnGetStartPersonalRating(this, playerGuid, personalRating); - // xinef: sync query // Try to get player's match maker rating from db and fall back to config setting if not found CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_MATCH_MAKER_RATING); @@ -177,6 +175,8 @@ bool ArenaTeam::AddMember(ObjectGuid playerGuid) newMember.MatchMakerRating = matchMakerRating; newMember.MaxMMR = maxMMR; + sScriptMgr->OnAddMember(this, newMember); + Members.push_back(newMember); sCharacterCache->UpdateCharacterArenaTeamId(playerGuid, GetSlot(), GetId()); @@ -184,7 +184,11 @@ bool ArenaTeam::AddMember(ObjectGuid playerGuid) stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_ARENA_TEAM_MEMBER); stmt->SetData(0, TeamId); stmt->SetData(1, playerGuid.GetCounter()); - stmt->SetData(2, personalRating); + stmt->SetData(2, newMember.WeekGames); + stmt->SetData(3, newMember.WeekWins); + stmt->SetData(4, newMember.SeasonGames); + stmt->SetData(5, newMember.SeasonWins); + stmt->SetData(6, newMember.PersonalRating); CharacterDatabase.Execute(stmt); // Inform player if online diff --git a/src/server/game/Battlegrounds/ArenaTeamMgr.cpp b/src/server/game/Battlegrounds/ArenaTeamMgr.cpp index b3fb4d71a4..6451817771 100644 --- a/src/server/game/Battlegrounds/ArenaTeamMgr.cpp +++ b/src/server/game/Battlegrounds/ArenaTeamMgr.cpp @@ -268,6 +268,8 @@ void ArenaTeamMgr::DistributeArenaPoints() } } + sScriptMgr->OnArenaWeekReset(); + ChatHandler(nullptr).SendWorldText(LANG_DIST_ARENA_POINTS_TEAM_END); ChatHandler(nullptr).SendWorldText(LANG_DIST_ARENA_POINTS_END); diff --git a/src/server/game/Scripting/ScriptDefines/ArenaScript.cpp b/src/server/game/Scripting/ScriptDefines/ArenaScript.cpp index 0dc6fc19ea..91bc19bd8e 100644 --- a/src/server/game/Scripting/ScriptDefines/ArenaScript.cpp +++ b/src/server/game/Scripting/ScriptDefines/ArenaScript.cpp @@ -54,9 +54,9 @@ bool ScriptMgr::CanSaveArenaStatsForMember(ArenaTeam* team, ObjectGuid playerGui CALL_ENABLED_BOOLEAN_HOOKS(ArenaScript, ARENAHOOK_CAN_SAVE_ARENA_STATS_FOR_MEMBER, !script->CanSaveArenaStatsForMember(team, playerGuid)); } -void ScriptMgr::OnGetStartPersonalRating(ArenaTeam* team, ObjectGuid playerGuid, uint32& personalRating) +void ScriptMgr::OnAddMember(ArenaTeam* team, ArenaTeamMember& member) { - CALL_ENABLED_HOOKS(ArenaScript, ARENAHOOK_ON_GET_START_PERSONAL_RATING, script->OnGetStartPersonalRating(team, playerGuid, personalRating)); + CALL_ENABLED_HOOKS(ArenaScript, ARENAHOOK_ON_ADD_MEMBER, script->OnAddMember(team, member)); } ArenaScript::ArenaScript(char const* name, std::vector enabledHooks) diff --git a/src/server/game/Scripting/ScriptDefines/ArenaScript.h b/src/server/game/Scripting/ScriptDefines/ArenaScript.h index cb71c7127b..48b3fae1e1 100644 --- a/src/server/game/Scripting/ScriptDefines/ArenaScript.h +++ b/src/server/game/Scripting/ScriptDefines/ArenaScript.h @@ -22,6 +22,8 @@ #include "ScriptObject.h" #include +struct ArenaTeamMember; + enum ArenaHook { ARENAHOOK_CAN_ADD_MEMBER, @@ -31,7 +33,7 @@ enum ArenaHook ARENAHOOK_ON_ARENA_START, ARENAHOOK_ON_BEFORE_TEAM_MEMBER_UPDATE, ARENAHOOK_CAN_SAVE_ARENA_STATS_FOR_MEMBER, - ARENAHOOK_ON_GET_START_PERSONAL_RATING, + ARENAHOOK_ON_ADD_MEMBER, ARENAHOOK_END }; @@ -59,9 +61,13 @@ public: [[nodiscard]] virtual bool CanSaveArenaStatsForMember(ArenaTeam* /*team*/, ObjectGuid /*playerGuid*/) { return true; } - // Called with the personal rating the core computed for a player joining the team, before it is - // stored on the member and written to arena_team_member. - virtual void OnGetStartPersonalRating(ArenaTeam* /*team*/, ObjectGuid /*playerGuid*/, uint32& /*personalRating*/) { } + // Called with the fully built member right before it is added to the team and written to + // arena_team_member. The personal rating and the week/season counters left on the struct are + // what the team holds and what is stored. MatchMakerRating and MaxMMR are held in memory but + // not written here, only by the next ArenaTeam::SaveToDB; Guid, Name and Class are identity, + // and changing them desyncs the member from its row and the character cache. + // Cannot veto the join, CanAddMember does that. + virtual void OnAddMember(ArenaTeam* /*team*/, ArenaTeamMember& /*member*/) { } }; #endif diff --git a/src/server/game/Scripting/ScriptDefines/GlobalScript.cpp b/src/server/game/Scripting/ScriptDefines/GlobalScript.cpp index b995cb573d..44738812cb 100644 --- a/src/server/game/Scripting/ScriptDefines/GlobalScript.cpp +++ b/src/server/game/Scripting/ScriptDefines/GlobalScript.cpp @@ -74,6 +74,11 @@ void ScriptMgr::OnBeforeUpdateArenaPoints(ArenaTeam* at, std::mapOnBeforeUpdateArenaPoints(at, ap)); } +void ScriptMgr::OnArenaWeekReset() +{ + CALL_ENABLED_HOOKS(GlobalScript, GLOBALHOOK_ON_ARENA_WEEK_RESET, script->OnArenaWeekReset()); +} + void ScriptMgr::OnAfterUpdateEncounterState(Map* map, EncounterCreditType type, uint32 creditEntry, Unit* source, Difficulty difficulty_fixed, DungeonEncounterList const* encounters, uint32 dungeonCompleted, bool updated) { CALL_ENABLED_HOOKS(GlobalScript, GLOBALHOOK_ON_AFTER_UPDATE_ENCOUNTER_STATE, script->OnAfterUpdateEncounterState(map, type, creditEntry, source, difficulty_fixed, encounters, dungeonCompleted, updated)); diff --git a/src/server/game/Scripting/ScriptDefines/GlobalScript.h b/src/server/game/Scripting/ScriptDefines/GlobalScript.h index cd3dc14060..d711b806fa 100644 --- a/src/server/game/Scripting/ScriptDefines/GlobalScript.h +++ b/src/server/game/Scripting/ScriptDefines/GlobalScript.h @@ -37,6 +37,7 @@ enum GlobalHook GLOBALHOOK_ON_INITIALIZE_LOCKED_DUNGEONS, GLOBALHOOK_ON_AFTER_INITIALIZE_LOCKED_DUNGEONS, GLOBALHOOK_ON_BEFORE_UPDATE_ARENA_POINTS, + GLOBALHOOK_ON_ARENA_WEEK_RESET, GLOBALHOOK_ON_AFTER_UPDATE_ENCOUNTER_STATE, GLOBALHOOK_ON_BEFORE_WORLDOBJECT_SET_PHASEMASK, GLOBALHOOK_ON_IS_AFFECTED_BY_SPELL_MOD_CHECK, @@ -73,6 +74,10 @@ public: // On Before arena points distribution virtual void OnBeforeUpdateArenaPoints(ArenaTeam* /*at*/, std::map& /*ap*/) { } + // Called when the weekly arena point distribution has just reset the week statistics of every + // arena team on the realm. + virtual void OnArenaWeekReset() { } + // Called when a dungeon encounter is updated. virtual void OnAfterUpdateEncounterState(Map* /*map*/, EncounterCreditType /*type*/, uint32 /*creditEntry*/, Unit* /*source*/, Difficulty /*difficulty_fixed*/, std::list const* /*encounters*/, uint32 /*dungeonCompleted*/, bool /*updated*/) { } diff --git a/src/server/game/Scripting/ScriptMgr.h b/src/server/game/Scripting/ScriptMgr.h index 748e1eb356..0f2429dfb7 100644 --- a/src/server/game/Scripting/ScriptMgr.h +++ b/src/server/game/Scripting/ScriptMgr.h @@ -522,6 +522,7 @@ public: /* GlobalScript */ void OnGlobalItemDelFromDB(CharacterDatabaseTransaction trans, ObjectGuid::LowType itemGuid); void OnGlobalMirrorImageDisplayItem(Item const* item, uint32& display); void OnBeforeUpdateArenaPoints(ArenaTeam* at, std::map& ap); + void OnArenaWeekReset(); void OnAfterRefCount(Player const* player, Loot& loot, bool canRate, uint16 lootMode, LootStoreItem* LootStoreItem, uint32& maxcount, LootStore const& store); void OnAfterCalculateLootGroupAmount(Player const* player, Loot& loot, uint16 lootMode, uint32& groupAmount, LootStore const& store); void OnBeforeDropAddItem(Player const* player, Loot& loot, bool canRate, uint16 lootMode, LootStoreItem* LootStoreItem, LootStore const& store); @@ -675,7 +676,7 @@ public: /* ArenaScript */ void OnArenaStart(Battleground* const bg); bool OnBeforeArenaTeamMemberUpdate(ArenaTeam* team, Player* player, bool won, uint32 opponentMatchmakerRating, int32 matchmakerChange); bool CanSaveArenaStatsForMember(ArenaTeam* team, ObjectGuid playerGuid); - void OnGetStartPersonalRating(ArenaTeam* team, ObjectGuid playerGuid, uint32& personalRating); + void OnAddMember(ArenaTeam* team, ArenaTeamMember& member); public: /* MiscScript */ diff --git a/src/test/server/game/Battlegrounds/ArenaHookDefaultsTest.cpp b/src/test/server/game/Battlegrounds/ArenaHookDefaultsTest.cpp index 044de43f60..3357e55b93 100644 --- a/src/test/server/game/Battlegrounds/ArenaHookDefaultsTest.cpp +++ b/src/test/server/game/Battlegrounds/ArenaHookDefaultsTest.cpp @@ -92,14 +92,27 @@ TEST_F(ArenaHookDefaultsTest, CanSaveArenaStatsForMemberDefaultsTrue) EXPECT_TRUE(sScriptMgr->CanSaveArenaStatsForMember(&team, ObjectGuid::Empty)); } -// OnGetStartPersonalRating must leave the rating untouched by default so a new -// arena team member keeps the value ArenaTeam::AddMember computed for them. -TEST_F(ArenaHookDefaultsTest, OnGetStartPersonalRatingKeepsCoreValue) +// OnAddMember must leave the member untouched by default so a new arena team +// member keeps the rating and counters ArenaTeam::AddMember built for them, +// which are also what is written to arena_team_member. +TEST_F(ArenaHookDefaultsTest, OnAddMemberLeavesMemberUntouched) { ArenaTeam team; - uint32 personalRating = 1000; - sScriptMgr->OnGetStartPersonalRating(&team, ObjectGuid::Empty, personalRating); - EXPECT_EQ(personalRating, 1000u); + + ArenaTeamMember member{}; + member.PersonalRating = 1000; + member.WeekGames = 6; + member.WeekWins = 3; + member.SeasonGames = 40; + member.SeasonWins = 22; + + sScriptMgr->OnAddMember(&team, member); + + EXPECT_EQ(member.PersonalRating, 1000u); + EXPECT_EQ(member.WeekGames, 6u); + EXPECT_EQ(member.WeekWins, 3u); + EXPECT_EQ(member.SeasonGames, 40u); + EXPECT_EQ(member.SeasonWins, 22u); } // OnBeforeArenaCheckWinConditions must return true by default so