From 0bfef874de53ce9e4501dc0c9b498cb07e2ce30f Mon Sep 17 00:00:00 2001 From: hondacrx Date: Wed, 3 Nov 2021 11:44:33 -0400 Subject: [PATCH] Core/Conversations: Dropped time fields and calculate them using db2 data Port From (https://github.com/TrinityCore/TrinityCore/commit/94b14e4f6355a7bab8004dcc2ecadadbc52833de) --- Source/Framework/Constants/SharedConst.cs | 37 ++++++++++++ .../Database/Databases/HotfixDatabase.cs | 5 ++ Source/Game/DataStorage/CliDB.cs | 2 + .../DataStorage/ConversationDataStorage.cs | 16 ++---- Source/Game/DataStorage/DB2Manager.cs | 10 ++++ Source/Game/DataStorage/Structs/B_Records.cs | 8 +++ Source/Game/Entities/Conversation.cs | 57 +++++++++++++++---- .../Entities/Object/Update/UpdateFields.cs | 26 +++++++-- .../master/2021_10_26_00_hotfixes.sql | 15 +++++ .../world/master/2021_10_26_00_world.sql | 3 + 10 files changed, 153 insertions(+), 26 deletions(-) create mode 100644 sql/updates/hotfixes/master/2021_10_26_00_hotfixes.sql create mode 100644 sql/updates/world/master/2021_10_26_00_world.sql diff --git a/Source/Framework/Constants/SharedConst.cs b/Source/Framework/Constants/SharedConst.cs index 9197707b4..5b9c8ecbe 100644 --- a/Source/Framework/Constants/SharedConst.cs +++ b/Source/Framework/Constants/SharedConst.cs @@ -400,6 +400,22 @@ namespace Framework.Constants return locale < Locale.Total && locale != Locale.None; } + public static CascLocaleBit[] WowLocaleToCascLocaleBit = + { + CascLocaleBit.enUS, + CascLocaleBit.koKR, + CascLocaleBit.frFR, + CascLocaleBit.deDE, + CascLocaleBit.zhCN, + CascLocaleBit.zhTW, + CascLocaleBit.esES, + CascLocaleBit.esMX, + CascLocaleBit.ruRU, + CascLocaleBit.None, + CascLocaleBit.ptBR, + CascLocaleBit.itIT + }; + public static long GetMaskForRace(Race raceId) { return raceId < Race.Max && raceBits[(int)raceId] >= 0 && raceBits[(int)raceId] < 64 ? (1 << raceBits[(int)raceId]) : 0; @@ -423,6 +439,27 @@ namespace Framework.Constants Total = 12 } + public enum CascLocaleBit + { + None = 0, + enUS = 1, + koKR = 2, + Reserved = 3, + frFR = 4, + deDE = 5, + zhCN = 6, + esES = 7, + zhTW = 8, + enGB = 9, + enCN = 10, + enTW = 11, + esMX = 12, + ruRU = 13, + ptBR = 14, + itIT = 15, + ptPT = 16 + } + public enum ComparisionType { EQ = 0, diff --git a/Source/Framework/Database/Databases/HotfixDatabase.cs b/Source/Framework/Database/Databases/HotfixDatabase.cs index 71f81f7b4..98745eef2 100644 --- a/Source/Framework/Database/Databases/HotfixDatabase.cs +++ b/Source/Framework/Database/Databases/HotfixDatabase.cs @@ -191,6 +191,9 @@ namespace Framework.Database "SoundEntriesID1, SoundEntriesID2, EmoteID1, EmoteID2, EmoteID3, EmoteDelay1, EmoteDelay2, EmoteDelay3 FROM broadcast_text"); PrepareStatement(HotfixStatements.SEL_BROADCAST_TEXT_LOCALE, "SELECT ID, Text_lang, Text1_lang FROM broadcast_text_locale WHERE locale = ?"); + // BroadcastTextDuration.db2 + PrepareStatement(HotfixStatements.SEL_BROADCAST_TEXT_DURATION, "SELECT ID, BroadcastTextID, Locale, Duration FROM broadcast_text_duration"); + // CfgRegions.db2 PrepareStatement(HotfixStatements.SEL_CFG_REGIONS, "SELECT ID, Tag, RegionID, Raidorigin, RegionGroupMask, ChallengeOrigin FROM cfg_regions"); @@ -1301,6 +1304,8 @@ namespace Framework.Database SEL_BROADCAST_TEXT, SEL_BROADCAST_TEXT_LOCALE, + SEL_BROADCAST_TEXT_DURATION, + SEL_CFG_REGIONS, SEL_CHAR_TITLES, diff --git a/Source/Game/DataStorage/CliDB.cs b/Source/Game/DataStorage/CliDB.cs index dfdc5b5bd..e1d4bc2f2 100644 --- a/Source/Game/DataStorage/CliDB.cs +++ b/Source/Game/DataStorage/CliDB.cs @@ -93,6 +93,7 @@ namespace Game.DataStorage BattlePetSpeciesStateStorage = ReadDB2("BattlePetSpeciesState.db2", HotfixStatements.SEL_BATTLE_PET_SPECIES_STATE); BattlemasterListStorage = ReadDB2("BattlemasterList.db2", HotfixStatements.SEL_BATTLEMASTER_LIST, HotfixStatements.SEL_BATTLEMASTER_LIST_LOCALE); BroadcastTextStorage = ReadDB2("BroadcastText.db2", HotfixStatements.SEL_BROADCAST_TEXT, HotfixStatements.SEL_BROADCAST_TEXT_LOCALE); + BroadcastTextDurationStorage = ReadDB2("BroadcastTextDuration.db2", HotfixStatements.SEL_BROADCAST_TEXT_DURATION); CfgRegionsStorage = ReadDB2("Cfg_Regions.db2", HotfixStatements.SEL_CFG_REGIONS); CharTitlesStorage = ReadDB2("CharTitles.db2", HotfixStatements.SEL_CHAR_TITLES, HotfixStatements.SEL_CHAR_TITLES_LOCALE); CharacterLoadoutStorage = ReadDB2("CharacterLoadout.db2", HotfixStatements.SEL_CHARACTER_LOADOUT); @@ -466,6 +467,7 @@ namespace Game.DataStorage public static DB6Storage BattlePetSpeciesStateStorage; public static DB6Storage BattlemasterListStorage; public static DB6Storage BroadcastTextStorage; + public static DB6Storage BroadcastTextDurationStorage; public static DB6Storage CfgRegionsStorage; public static DB6Storage CharTitlesStorage; public static DB6Storage CharacterLoadoutStorage; diff --git a/Source/Game/DataStorage/ConversationDataStorage.cs b/Source/Game/DataStorage/ConversationDataStorage.cs index 6c6f21166..e4741cf65 100644 --- a/Source/Game/DataStorage/ConversationDataStorage.cs +++ b/Source/Game/DataStorage/ConversationDataStorage.cs @@ -35,7 +35,7 @@ namespace Game.DataStorage Dictionary actorsByConversation = new(); Dictionary actorGuidsByConversation = new(); - SQLResult lineTemplates = DB.World.Query("SELECT Id, StartTime, UiCameraID, ActorIdx, Flags FROM conversation_line_template"); + SQLResult lineTemplates = DB.World.Query("SELECT Id, UiCameraID, ActorIdx, Flags FROM conversation_line_template"); if (!lineTemplates.IsEmpty()) { uint oldMSTime = Time.GetMSTime(); @@ -52,10 +52,9 @@ namespace Game.DataStorage ConversationLineTemplate conversationLine = new(); conversationLine.Id = id; - conversationLine.StartTime = lineTemplates.Read(1); - conversationLine.UiCameraID = lineTemplates.Read(2); - conversationLine.ActorIdx = lineTemplates.Read(3); - conversationLine.Flags = lineTemplates.Read(4); + conversationLine.UiCameraID = lineTemplates.Read(1); + conversationLine.ActorIdx = lineTemplates.Read(2); + conversationLine.Flags = lineTemplates.Read(3); _conversationLineTemplateStorage[id] = conversationLine; } @@ -140,7 +139,7 @@ namespace Game.DataStorage Log.outInfo(LogFilter.ServerLoading, "Loaded 0 Conversation actors. DB table `conversation_actors` is empty."); } - SQLResult templateResult = DB.World.Query("SELECT Id, FirstLineId, LastLineEndTime, TextureKitId, ScriptName FROM conversation_template"); + SQLResult templateResult = DB.World.Query("SELECT Id, FirstLineId, TextureKitId, ScriptName FROM conversation_template"); if (!templateResult.IsEmpty()) { uint oldMSTime = Time.GetMSTime(); @@ -150,8 +149,7 @@ namespace Game.DataStorage ConversationTemplate conversationTemplate = new(); conversationTemplate.Id = templateResult.Read(0); conversationTemplate.FirstLineId = templateResult.Read(1); - conversationTemplate.LastLineEndTime = templateResult.Read(2); - conversationTemplate.TextureKitId = templateResult.Read(3); + conversationTemplate.TextureKitId = templateResult.Read(2); conversationTemplate.ScriptId = Global.ObjectMgr.GetScriptId(templateResult.Read(3)); conversationTemplate.Actors = actorsByConversation.TryGetValue(conversationTemplate.Id, out var actors) ? actors.ToList() : null; @@ -211,7 +209,6 @@ namespace Game.DataStorage public class ConversationLineTemplate { public uint Id; // Link to ConversationLine.db2 - public uint StartTime; // Time in ms after conversation creation the line is displayed public uint UiCameraID; // Link to UiCamera.db2 public byte ActorIdx; // Index from conversation_actors public byte Flags; @@ -222,7 +219,6 @@ namespace Game.DataStorage { public uint Id; public uint FirstLineId; // Link to ConversationLine.db2 - public uint LastLineEndTime; // Time in ms after conversation creation the last line fades out public uint TextureKitId; // Background texture public uint ScriptId; diff --git a/Source/Game/DataStorage/DB2Manager.cs b/Source/Game/DataStorage/DB2Manager.cs index c5f37bad3..91e7f1b2b 100644 --- a/Source/Game/DataStorage/DB2Manager.cs +++ b/Source/Game/DataStorage/DB2Manager.cs @@ -123,6 +123,10 @@ namespace Game.DataStorage } } + foreach (var broadcastTextDuration in BroadcastTextDurationStorage.Values) + _broadcastTextDurations[(broadcastTextDuration.BroadcastTextID, (CascLocaleBit)broadcastTextDuration.Locale)] = broadcastTextDuration.Duration; + + foreach (var uiDisplay in ChrClassUIDisplayStorage.Values) { Cypher.Assert(uiDisplay.ChrClassesID < (byte)Class.Max); @@ -943,6 +947,11 @@ namespace Game.DataStorage return broadcastText.Text[SharedConst.DefaultLocale]; } + public int GetBroadcastTextDuration(int broadcastTextId, Locale locale = Locale.enUS) + { + return _broadcastTextDurations.LookupByKey((broadcastTextId, SharedConst.WowLocaleToCascLocaleBit[(int)locale])); + } + public ChrClassUIDisplayRecord GetUiDisplayForClass(Class unitClass) { Cypher.Assert(unitClass < Class.Max); @@ -2306,6 +2315,7 @@ namespace Game.DataStorage MultiMap _azeritePowers = new(); Dictionary<(uint azeriteUnlockSetId, ItemContext itemContext), byte[]> _azeriteTierUnlockLevels = new(); Dictionary<(uint itemId, ItemContext itemContext), AzeriteUnlockMappingRecord> _azeriteUnlockMappings = new(); + Dictionary<(int broadcastTextId, CascLocaleBit cascLocaleBit), int> _broadcastTextDurations = new(); ChrClassUIDisplayRecord[] _uiDisplayByClass = new ChrClassUIDisplayRecord[(int)Class.Max]; uint[][] _powersByClass = new uint[(int)Class.Max][]; MultiMap _chrCustomizationChoicesByOption = new(); diff --git a/Source/Game/DataStorage/Structs/B_Records.cs b/Source/Game/DataStorage/Structs/B_Records.cs index 97a1797ed..d0b771307 100644 --- a/Source/Game/DataStorage/Structs/B_Records.cs +++ b/Source/Game/DataStorage/Structs/B_Records.cs @@ -121,4 +121,12 @@ namespace Game.DataStorage public ushort[] EmoteID = new ushort[3]; public ushort[] EmoteDelay = new ushort[3]; } + + public sealed class BroadcastTextDurationRecord + { + public uint Id; + public int BroadcastTextID; + public int Locale; + public int Duration; + } } diff --git a/Source/Game/Entities/Conversation.cs b/Source/Game/Entities/Conversation.cs index a76bdbf1c..9667da1b8 100644 --- a/Source/Game/Entities/Conversation.cs +++ b/Source/Game/Entities/Conversation.cs @@ -18,9 +18,11 @@ using Framework.Constants; using Game.DataStorage; using Game.Maps; -using Game.Spells; -using System.Collections.Generic; using Game.Networking; +using Game.Spells; +using System; +using System.Collections.Generic; +using System.Linq; namespace Game.Entities { @@ -28,8 +30,6 @@ namespace Game.Entities { public Conversation() : base(false) { - _duration = 0; - ObjectTypeMask |= TypeMask.Conversation; ObjectTypeId = TypeId.Conversation; @@ -61,9 +61,9 @@ namespace Game.Entities public override void Update(uint diff) { - if (GetDuration() > diff) + if (GetDuration() > TimeSpan.FromMilliseconds(diff)) { - _duration -= diff; + _duration -= TimeSpan.FromMilliseconds(diff); DoWithSuppressingObjectUpdates(() => { // Only sent in CreateObject @@ -121,8 +121,6 @@ namespace Game.Entities SetEntry(conversationEntry); SetObjectScale(1.0f); - SetUpdateFieldValue(m_values.ModifyValue(m_conversationData).ModifyValue(m_conversationData.LastLineEndTime), conversationTemplate.LastLineEndTime); - _duration = conversationTemplate.LastLineEndTime + 10 * Time.InMilliseconds; _textureKitId = conversationTemplate.TextureKitId; if (conversationTemplate.Actors != null) @@ -171,16 +169,38 @@ namespace Game.Entities ConversationLine lineField = new(); lineField.ConversationLineID = line.Id; - lineField.StartTime = line.StartTime; lineField.UiCameraID = line.UiCameraID; lineField.ActorIndex = line.ActorIdx; lineField.Flags = line.Flags; + ConversationLineRecord convoLine = CliDB.ConversationLineStorage.LookupByKey(line.Id); // never null for conversationTemplate->Lines + + for (Locale locale = Locale.enUS; locale < Locale.Total; locale = locale + 1) + { + if (locale == Locale.None) + continue; + + _lineStartTimes[(locale, line.Id)] = _lastLineEndTimes[(int)locale]; + if (locale == Locale.enUS) + lineField.StartTime = (uint)_lastLineEndTimes[(int)locale].TotalMilliseconds; + + int broadcastTextDuration = Global.DB2Mgr.GetBroadcastTextDuration((int)convoLine.BroadcastTextID, locale); + if (broadcastTextDuration != 0) + _lastLineEndTimes[(int)locale] += TimeSpan.FromMilliseconds(broadcastTextDuration); + + _lastLineEndTimes[(int)locale] += TimeSpan.FromMilliseconds(convoLine.AdditionalDuration); + } + lines.Add(lineField); } + _duration = _lastLineEndTimes.Max(); + SetUpdateFieldValue(m_values.ModifyValue(m_conversationData).ModifyValue(m_conversationData.LastLineEndTime), (uint)_duration.TotalMilliseconds); SetUpdateFieldValue(m_values.ModifyValue(m_conversationData).ModifyValue(m_conversationData.Lines), lines); + // conversations are despawned 5-20s after LastLineEndTime + _duration += TimeSpan.FromSeconds(10); + Global.ScriptMgr.OnConversationCreate(this, creator); // All actors need to be set @@ -207,6 +227,16 @@ namespace Game.Entities SetUpdateFieldValue(ref actorField.Type, ConversationActorType.WorldObjectActor); } + public TimeSpan GetLineStartTime(Locale locale, int lineId) + { + return _lineStartTimes.LookupByKey((locale, lineId)); + } + + public TimeSpan GetLastLineEndTime(Locale locale) + { + return _lastLineEndTimes[(int)locale]; + } + public uint GetScriptId() { return Global.ConversationDataStorage.GetConversationTemplate(GetEntry()).ScriptId; @@ -274,13 +304,13 @@ namespace Game.Entities base.ClearUpdateMask(remove); } - uint GetDuration() { return _duration; } + TimeSpan GetDuration() { return _duration; } public uint GetTextureKitId() { return _textureKitId; } public ObjectGuid GetCreatorGuid() { return _creatorGuid; } public override ObjectGuid GetOwnerGUID() { return GetCreatorGuid(); } public override uint GetFaction() { return 0; } - + public override float GetStationaryX() { return _stationaryPosition.GetPositionX(); } public override float GetStationaryY() { return _stationaryPosition.GetPositionY(); } public override float GetStationaryZ() { return _stationaryPosition.GetPositionZ(); } @@ -291,7 +321,10 @@ namespace Game.Entities Position _stationaryPosition = new(); ObjectGuid _creatorGuid; - uint _duration; + TimeSpan _duration; uint _textureKitId; + + Dictionary<(Locale locale, uint lineId), TimeSpan> _lineStartTimes = new(); + TimeSpan[] _lastLineEndTimes = new TimeSpan[(int)Locale.Total]; } } diff --git a/Source/Game/Entities/Object/Update/UpdateFields.cs b/Source/Game/Entities/Object/Update/UpdateFields.cs index 96c782868..22da6ad09 100644 --- a/Source/Game/Entities/Object/Update/UpdateFields.cs +++ b/Source/Game/Entities/Object/Update/UpdateFields.cs @@ -5622,7 +5622,7 @@ namespace Game.Entities public void WriteCreate(WorldPacket data, Conversation owner, Player receiver) { data.WriteUInt32(ConversationLineID); - data.WriteUInt32(StartTime); + data.WriteUInt32(GetViewerStartTime(this, owner, receiver)); data.WriteUInt32(UiCameraID); data.WriteUInt8(ActorIndex); data.WriteUInt8(Flags); @@ -5632,12 +5632,24 @@ namespace Game.Entities public void WriteUpdate(WorldPacket data, bool ignoreChangesMask, Conversation owner, Player receiver) { data.WriteUInt32(ConversationLineID); - data.WriteUInt32(StartTime); + data.WriteUInt32(GetViewerStartTime(this, owner, receiver)); data.WriteUInt32(UiCameraID); data.WriteUInt8(ActorIndex); data.WriteUInt8(Flags); data.WriteUInt8(ChatType); } + + public uint GetViewerStartTime(ConversationLine conversationLine, Conversation conversation, Player receiver) + { + uint startTime = conversationLine.StartTime; + Locale locale = receiver.GetSession().GetSessionDbLocaleIndex(); + + TimeSpan localizedStartTime = conversation.GetLineStartTime(locale, (int)conversationLine.ConversationLineID); + if (localizedStartTime != TimeSpan.Zero) + startTime = (uint)localizedStartTime.TotalMilliseconds; + + return startTime; + } } public class ConversationActorField @@ -5685,7 +5697,7 @@ namespace Game.Entities public void WriteCreate(WorldPacket data, UpdateFieldFlag fieldVisibilityFlags, Conversation owner, Player receiver) { data.WriteInt32(((List)Lines).Count); - data.WriteUInt32(LastLineEndTime); + data.WriteUInt32(GetViewerLastLineEndTime(this, owner, receiver)); data.WriteUInt32(Progress); for (int i = 0; i < ((List)Lines).Count; ++i) { @@ -5751,7 +5763,7 @@ namespace Game.Entities } if (_changesMask[4]) { - data.WriteUInt32(LastLineEndTime); + data.WriteUInt32(GetViewerLastLineEndTime(this, owner, receiver)); } if (_changesMask[5]) { @@ -5770,5 +5782,11 @@ namespace Game.Entities ClearChangesMask(Progress); _changesMask.ResetAll(); } + + public uint GetViewerLastLineEndTime(ConversationData conversationLineData, Conversation conversation, Player receiver) + { + Locale locale = receiver.GetSession().GetSessionDbLocaleIndex(); + return (uint)conversation.GetLastLineEndTime(locale).TotalMilliseconds; + } } } diff --git a/sql/updates/hotfixes/master/2021_10_26_00_hotfixes.sql b/sql/updates/hotfixes/master/2021_10_26_00_hotfixes.sql new file mode 100644 index 000000000..54c1a8b93 --- /dev/null +++ b/sql/updates/hotfixes/master/2021_10_26_00_hotfixes.sql @@ -0,0 +1,15 @@ +-- +-- Table structure for table `broadcast_text_duration` +-- +DROP TABLE IF EXISTS `broadcast_text_duration`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `broadcast_text_duration` ( + `ID` int(10) unsigned NOT NULL DEFAULT '0', + `BroadcastTextID` int(11) NOT NULL DEFAULT '0', + `Locale` int(11) NOT NULL DEFAULT '0', + `Duration` int(11) NOT NULL DEFAULT '0', + `VerifiedBuild` int(11) NOT NULL DEFAULT '0', + PRIMARY KEY (`ID`,`VerifiedBuild`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; diff --git a/sql/updates/world/master/2021_10_26_00_world.sql b/sql/updates/world/master/2021_10_26_00_world.sql new file mode 100644 index 000000000..e1f10ae3f --- /dev/null +++ b/sql/updates/world/master/2021_10_26_00_world.sql @@ -0,0 +1,3 @@ +-- +ALTER TABLE `conversation_line_template` DROP `StartTime`; +ALTER TABLE `conversation_template` DROP `LastLineEndTime`;