From dbef0aaec84ed868dcd2f7063a41dbb12880df85 Mon Sep 17 00:00:00 2001 From: killerwife Date: Sun, 19 Nov 2023 23:39:06 +0100 Subject: [PATCH 1/4] Implement SaveExistingSpawns and lookup of existing spawns --- WowPacketParser/Misc/Settings.cs | 1 + WowPacketParser/SQL/Builder.cs | 4 +- WowPacketParser/SQL/Builders/Spawns.cs | 33 +++++++- WowPacketParser/SQL/QueryBuilder.cs | 12 ++- WowPacketParser/SQL/SQLDatabase.cs | 76 ++++++++++++++++++- WowPacketParser/Store/Objects/GameObject.cs | 6 ++ WowPacketParser/Store/Objects/GameObjectDB.cs | 36 +++++++++ WowPacketParser/Store/Objects/WoWObject.cs | 5 ++ 8 files changed, 164 insertions(+), 9 deletions(-) create mode 100644 WowPacketParser/Store/Objects/GameObjectDB.cs diff --git a/WowPacketParser/Misc/Settings.cs b/WowPacketParser/Misc/Settings.cs index 1a990c01b9..66db8ddf1d 100644 --- a/WowPacketParser/Misc/Settings.cs +++ b/WowPacketParser/Misc/Settings.cs @@ -21,6 +21,7 @@ public static class Settings public static readonly ulong SQLOutputFlag = GetSQLOutputFlag(); public static readonly bool SQLOrderByKey = Conf.GetBoolean("SqlOrderByKey", false); public static readonly bool SaveTempSpawns = Conf.GetBoolean("SaveTempSpawns", false); + public static readonly bool SaveExistingSpawns = Conf.GetBoolean("SaveExistingSpawns", false); public static readonly bool SkipOnlyVerifiedBuildUpdateRows = Conf.GetBoolean("SkipOnlyVerifiedBuildUpdateRows", false); public static readonly bool SkipRowsWithFallbackValues = Conf.GetBoolean("SkipRowsWithFallbackValues", true); public static readonly bool IgnoreZeroValues = Conf.GetBoolean("IgnoreZeroValues", false); diff --git a/WowPacketParser/SQL/Builder.cs b/WowPacketParser/SQL/Builder.cs index e23d71cd00..a53b7248cc 100644 --- a/WowPacketParser/SQL/Builder.cs +++ b/WowPacketParser/SQL/Builder.cs @@ -175,9 +175,9 @@ private static List GetExpectedTargetDatabasesForExpansion(Cli case ClientType.Legion: return new List { TargetedDatabase.Legion }; case ClientType.BattleForAzeroth: // == ClientType.Classic - return new List { TargetedDatabase.BattleForAzeroth, TargetedDatabase.Classic }; + return new List { TargetedDatabase.BattleForAzeroth, TargetedDatabase.Classic, TargetedDatabase.TheBurningCrusade }; case ClientType.Shadowlands: // == ClientType.BurningCrusadeClassic - return new List { TargetedDatabase.Shadowlands, TargetedDatabase.Classic, TargetedDatabase.WotlkClassic }; + return new List { TargetedDatabase.Shadowlands, TargetedDatabase.Classic, TargetedDatabase.WotlkClassic, TargetedDatabase.TheBurningCrusade }; case ClientType.Dragonflight: return new List { TargetedDatabase.Dragonflight, TargetedDatabase.WotlkClassic }; default: diff --git a/WowPacketParser/SQL/Builders/Spawns.cs b/WowPacketParser/SQL/Builders/Spawns.cs index 5609d20276..0d542b30dd 100644 --- a/WowPacketParser/SQL/Builders/Spawns.cs +++ b/WowPacketParser/SQL/Builders/Spawns.cs @@ -30,7 +30,7 @@ private static bool GetTransportMap(WoWObject @object, out int mapId) if (transport.Type != ObjectType.GameObject) return false; - if (SQLConnector.Enabled) + if (SQLConnector.Enabled && Settings.TargetedDatabase != TargetedDatabase.TheBurningCrusade) { var transportTemplates = SQLDatabase.Get(new RowList { new GameObjectTemplate { Entry = (uint)transport.ObjectData.EntryID } }); if (transportTemplates.Count == 0) @@ -297,6 +297,11 @@ public static string Creature(Dictionary units) return result.ToString(); } + public static bool FloatComparison(float x, float y, float precision) + { + return Math.Abs(x - y) < precision; + } + [BuilderMethod(Gameobjects = true)] public static string GameObject(Dictionary gameObjects) { @@ -314,6 +319,20 @@ public static string GameObject(Dictionary gameObjects) ? gameObjects.Values.GroupBy(g => g, new SpawnComparer()).Select(x => x.First()) : gameObjects.Values.ToList(); + if (!Settings.SaveExistingSpawns && SQLConnector.Enabled) + { + var templatesDb = SQLDatabase.GetGameObjects(new RowList()); + var precision = 0.000001f; // warning - some zones shifted by 0.2 in some cases between later expansions + foreach (var go in gobList) + { + var staticRot = go.GetStaticRotation(); + var existingGo = templatesDb.Where(p => FloatComparison((float)p.Data.PosX, go.Movement.Position.X, precision) && FloatComparison((float)p.Data.PosY, go.Movement.Position.Y, precision) && FloatComparison((float)p.Data.PosZ, go.Movement.Position.Z, precision) && FloatComparison((float)p.Data.Ori, go.Movement.Orientation, precision) && + FloatComparison((float)p.Data.Rot0, staticRot.X, precision) && FloatComparison((float)p.Data.Rot1, staticRot.Y, precision) && FloatComparison((float)p.Data.Rot2, staticRot.Z, precision) && FloatComparison((float)p.Data.Rot3, staticRot.W, precision)).SingleOrDefault(); + if (existingGo != null) + go.ExistingDatabaseSpawn = true; + } + } + foreach (var go in gobList) { Row row = new Row(); @@ -456,6 +475,16 @@ public static string GameObject(Dictionary gameObjects) addonRow.Comment += " - !!! might be temporary spawn !!!"; } } + else if (go.ExistingDatabaseSpawn && !Settings.SaveExistingSpawns) + { + row.CommentOut = true; + row.Comment += " - !!! already present in database !!!"; + if (Settings.SQLOutputFlag.HasAnyFlagBit(SQLOutput.gameobject_addon)) + { + addonRow.CommentOut = true; + addonRow.Comment += " - !!! already present in database !!!"; + } + } else if (go.IsTransport()) { row.CommentOut = true; @@ -482,7 +511,7 @@ public static string GameObject(Dictionary gameObjects) } if (count == 0) - return String.Empty; + return string.Empty; StringBuilder result = new StringBuilder(); // delete query for GUIDs diff --git a/WowPacketParser/SQL/QueryBuilder.cs b/WowPacketParser/SQL/QueryBuilder.cs index 0d59139b4f..ef0bce957a 100644 --- a/WowPacketParser/SQL/QueryBuilder.cs +++ b/WowPacketParser/SQL/QueryBuilder.cs @@ -238,8 +238,16 @@ public string Build() foreach (var field in fields) { - fieldNames.Append(field.Item1); - fieldNames.Append(SQLUtil.CommaSeparator); + if (field.Item2.FieldType == typeof(decimal)) + { + fieldNames.Append($"round({field.Item1}, 20)"); + fieldNames.Append(SQLUtil.CommaSeparator); + } + else + { + fieldNames.Append(field.Item1); + fieldNames.Append(SQLUtil.CommaSeparator); + } } fieldNames.Remove(fieldNames.Length - 2, 2); // remove last ", " diff --git a/WowPacketParser/SQL/SQLDatabase.cs b/WowPacketParser/SQL/SQLDatabase.cs index d0ddbce175..7882604b93 100644 --- a/WowPacketParser/SQL/SQLDatabase.cs +++ b/WowPacketParser/SQL/SQLDatabase.cs @@ -133,6 +133,9 @@ private static void LoadBroadcastText() query = "SELECT ID, LanguageID, Text, Text1, EmoteID1, EmoteID2, EmoteID3, EmoteDelay1, EmoteDelay2, EmoteDelay3, SoundEntriesID, EmotesID, Flags " + $"FROM {Settings.TDBDatabase}.broadcast_text;"; + if (Settings.TargetedDatabase == TargetedDatabase.TheBurningCrusade) + return; + using (var command = SQLConnector.CreateCommand(query)) { if (command == null) @@ -203,6 +206,10 @@ private static void LoadPointsOfinterest() string query = "SELECT ID, PositionX, PositionY, Icon, Flags, Importance, Name " + $"FROM {Settings.TDBDatabase}.points_of_interest ORDER BY ID;"; + + if (Settings.TargetedDatabase == TargetedDatabase.TheBurningCrusade) + query = $"SELECT entry AS ID, x AS PositionX, y AS PositionY, icon AS Icon, flags AS Flags, data AS Importance, icon_name AS Name FROM {Settings.TDBDatabase}.points_of_interest ORDER BY entry;"; + using (var command = SQLConnector.CreateCommand(query)) { if (command == null) @@ -230,6 +237,9 @@ private static void LoadPointsOfinterest() private static void LoadCreatureEquipment() { + if (Settings.TargetedDatabase == TargetedDatabase.TheBurningCrusade) + return; + string columns = "CreatureID, ID, ItemID1, ItemID2, ItemID3, VerifiedBuild"; if (Settings.TargetedDatabase >= TargetedDatabase.Legion) columns += ", AppearanceModID1, ItemVisual1, AppearanceModID2, ItemVisual2, AppearanceModID3, ItemVisual3"; @@ -277,6 +287,9 @@ private static void LoadCreatureEquipment() private static void LoadNPCTexts() { + if (Settings.TargetedDatabase == TargetedDatabase.TheBurningCrusade) + return; + string columns = "ID, BroadcastTextID0, BroadcastTextID1, BroadcastTextID2, BroadcastTextID3, BroadcastTextID4, BroadcastTextID5, BroadcastTextID6, BroadcastTextID7"; string query = $"SELECT {columns} FROM {Settings.TDBDatabase}.npc_text"; @@ -304,6 +317,9 @@ private static void LoadNPCTexts() private static void LoadGossipMenuNPCTexts() { + if (Settings.TargetedDatabase == TargetedDatabase.TheBurningCrusade) + return; + string columns = "MenuID, TextID"; string query = $"SELECT {columns} FROM {Settings.TDBDatabase}.gossip_menu"; @@ -328,12 +344,18 @@ private static void LoadGossipMenuNPCTexts() private static void LoadWorldStates() { - if (Settings.TargetedDatabase != TargetedDatabase.Cataclysm && (Settings.TargetedDatabase < TargetedDatabase.Shadowlands || Settings.TargetedDatabase >= TargetedDatabase.Classic)) + if (Settings.TargetedDatabase != TargetedDatabase.Cataclysm && Settings.TargetedDatabase != TargetedDatabase.TheBurningCrusade && (Settings.TargetedDatabase < TargetedDatabase.Shadowlands || Settings.TargetedDatabase >= TargetedDatabase.Classic)) return; string columns = "`ID`, `Comment`"; string query = $"SELECT {columns} FROM {Settings.TDBDatabase}.world_state"; + if (Settings.TargetedDatabase == TargetedDatabase.TheBurningCrusade) + { + columns = "`Id`, `Name`"; + query = $"SELECT {columns} FROM {Settings.TDBDatabase}.worldstate_name"; + } + using (var command = SQLConnector.CreateCommand(query)) { if (command == null) @@ -353,6 +375,12 @@ private static void LoadWorldStates() private static void LoadNameData() { + string questQuery = $"SELECT `ID`, `LogTitle` FROM {Settings.TDBDatabase}.quest_template;"; + if (Settings.TargetedDatabase == TargetedDatabase.TheBurningCrusade) + { + questQuery = $"SELECT `entry`, `Title` FROM {Settings.TDBDatabase}.quest_template;"; + } + // Unit NameStores.Add(StoreNameType.Unit, GetDict( $"SELECT `entry`, `name` FROM {Settings.TDBDatabase}.creature_template;")); @@ -362,8 +390,7 @@ private static void LoadNameData() $"SELECT `entry`, `name` FROM {Settings.TDBDatabase}.gameobject_template;")); // Quest - NameStores.Add(StoreNameType.Quest, GetDict( - $"SELECT `ID`, `LogTitle` FROM {Settings.TDBDatabase}.quest_template;")); + NameStores.Add(StoreNameType.Quest, GetDict(questQuery)); // Item - Cataclysm and above have ItemSparse.db2 if (Settings.TargetedDatabase <= TargetedDatabase.WrathOfTheLichKing) @@ -460,6 +487,49 @@ public static RowList Get(RowList rowList = null, string database = nul return result; } + public static RowList GetGameObjects(RowList rowList = null, string database = null) + { + if (!SQLConnector.Enabled) + return null; + + if (!SQLUtil.IsTableVisible()) + return null; + + var result = new RowList(); + + using (var command = SQLConnector.CreateCommand(new SQLSelect(rowList, database).Build())) + { + if (command == null) + return null; + + var fields = SQLUtil.GetFields(); + var fieldsCount = fields.Select(f => f.Item3.First().Count).Sum(); + using (MySqlDataReader reader = command.ExecuteReader()) + { + while (reader.Read()) + { + var go = new GameObjectDB(); + + go.DbGuid = reader.GetUInt32(0); + go.ID = reader.GetUInt32(1); + go.Map = reader.GetUInt32(2); + go.PosX = reader.GetDecimal(3); + go.PosY = reader.GetDecimal(4); + go.PosZ = reader.GetDecimal(5); + go.Ori = reader.GetDecimal(6); + go.Rot0 = reader.GetDecimal(7); + go.Rot1 = reader.GetDecimal(8); + go.Rot2 = reader.GetDecimal(9); + go.Rot3 = reader.GetDecimal(10); + + result.Add(go); + } + } + } + + return result; + } + public static uint GetNPCTextIDByMenuIDAndBroadcastText(int menuId, uint broadcastTextID) { if (!BroadcastToNPCTexts.TryGetValue(broadcastTextID, out var npcTextsByBroadcast)) diff --git a/WowPacketParser/Store/Objects/GameObject.cs b/WowPacketParser/Store/Objects/GameObject.cs index faaef0ac10..d55d06384a 100644 --- a/WowPacketParser/Store/Objects/GameObject.cs +++ b/WowPacketParser/Store/Objects/GameObject.cs @@ -11,6 +11,7 @@ public sealed record GameObject : WoWObject, IDataModel public IGameObjectData GameObjectData; public uint? WorldEffectID; public uint? AIAnimKitID; + public bool ExistingDatabaseSpawn { get; set; } public GameObject() : base() { @@ -27,6 +28,11 @@ public override bool IsTemporarySpawn() return !GameObjectData.CreatedBy.IsEmpty(); } + public override bool IsExistingSpawn() + { + return ExistingDatabaseSpawn; + } + public Quaternion GetStaticRotation() { if (ClientVersion.AddedInVersion(ClientVersionBuild.V3_1_0_9767)) diff --git a/WowPacketParser/Store/Objects/GameObjectDB.cs b/WowPacketParser/Store/Objects/GameObjectDB.cs new file mode 100644 index 0000000000..518c854291 --- /dev/null +++ b/WowPacketParser/Store/Objects/GameObjectDB.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using WowPacketParser.SQL; + +namespace WowPacketParser.Store.Objects +{ + [DBTableName("gameobject")] + public sealed record GameObjectDB : IDataModel + { + [DBFieldName("guid", true)] + public uint DbGuid; + [DBFieldName("id")] + public uint ID; + [DBFieldName("map")] + public uint Map; + [DBFieldName("position_x")] + public decimal PosX; + [DBFieldName("position_y")] + public decimal PosY; + [DBFieldName("position_z")] + public decimal PosZ; + [DBFieldName("orientation")] + public decimal Ori; + [DBFieldName("rotation0")] + public decimal Rot0; + [DBFieldName("rotation1")] + public decimal Rot1; + [DBFieldName("rotation2")] + public decimal Rot2; + [DBFieldName("rotation3")] + public decimal Rot3; + } +} diff --git a/WowPacketParser/Store/Objects/WoWObject.cs b/WowPacketParser/Store/Objects/WoWObject.cs index 8005a5969e..3c1deacc4a 100644 --- a/WowPacketParser/Store/Objects/WoWObject.cs +++ b/WowPacketParser/Store/Objects/WoWObject.cs @@ -44,6 +44,11 @@ public virtual bool IsTemporarySpawn() return ForceTemporarySpawn; } + public virtual bool IsExistingSpawn() + { + return false; + } + public bool IsOnTransport() { return Movement.Transport != null && !Movement.Transport.Guid.IsEmpty(); From 789b3f808163066382d598eb914de6fea3b9f252 Mon Sep 17 00:00:00 2001 From: killerwife Date: Mon, 20 Nov 2023 19:50:49 +0100 Subject: [PATCH 2/4] Minor bugfix --- WowPacketParser/SQL/Builders/Spawns.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WowPacketParser/SQL/Builders/Spawns.cs b/WowPacketParser/SQL/Builders/Spawns.cs index 0d542b30dd..64a3ba0f7a 100644 --- a/WowPacketParser/SQL/Builders/Spawns.cs +++ b/WowPacketParser/SQL/Builders/Spawns.cs @@ -327,7 +327,7 @@ public static string GameObject(Dictionary gameObjects) { var staticRot = go.GetStaticRotation(); var existingGo = templatesDb.Where(p => FloatComparison((float)p.Data.PosX, go.Movement.Position.X, precision) && FloatComparison((float)p.Data.PosY, go.Movement.Position.Y, precision) && FloatComparison((float)p.Data.PosZ, go.Movement.Position.Z, precision) && FloatComparison((float)p.Data.Ori, go.Movement.Orientation, precision) && - FloatComparison((float)p.Data.Rot0, staticRot.X, precision) && FloatComparison((float)p.Data.Rot1, staticRot.Y, precision) && FloatComparison((float)p.Data.Rot2, staticRot.Z, precision) && FloatComparison((float)p.Data.Rot3, staticRot.W, precision)).SingleOrDefault(); + FloatComparison((float)p.Data.Rot0, staticRot.X, precision) && FloatComparison((float)p.Data.Rot1, staticRot.Y, precision) && FloatComparison((float)p.Data.Rot2, staticRot.Z, precision) && FloatComparison((float)p.Data.Rot3, staticRot.W, precision)).FirstOrDefault(); if (existingGo != null) go.ExistingDatabaseSpawn = true; } From 8978ed8d5c309a3acdbe5a6f9eae35da64476f0f Mon Sep 17 00:00:00 2001 From: ModoX Date: Wed, 27 Dec 2023 17:48:53 +0100 Subject: [PATCH 3/4] prepare merge --- WowPacketParser/App.config | 7 +++ WowPacketParser/SQL/Builders/Spawns.cs | 58 +++++++++++++++---- WowPacketParser/SQL/SQLDatabase.cs | 41 ++++++++++++- WowPacketParser/Store/Objects/CreatureDB.cs | 23 ++++++++ WowPacketParser/Store/Objects/GameObjectDB.cs | 9 +-- WowPacketParser/Store/Objects/Unit.cs | 6 ++ 6 files changed, 126 insertions(+), 18 deletions(-) create mode 100644 WowPacketParser/Store/Objects/CreatureDB.cs diff --git a/WowPacketParser/App.config b/WowPacketParser/App.config index 14e26c6ecd..f0ff8bca83 100644 --- a/WowPacketParser/App.config +++ b/WowPacketParser/App.config @@ -268,6 +268,13 @@ --> + + + - - +