Skip to content

Commit

Permalink
Added basic SQL output for spawn tracking (TrinityCore#927)
Browse files Browse the repository at this point in the history
  • Loading branch information
meji46 authored Feb 25, 2025
1 parent 200b0d2 commit 6440018
Show file tree
Hide file tree
Showing 89 changed files with 699 additions and 201 deletions.
6 changes: 6 additions & 0 deletions WowPacketParser/DBC/DBC.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ public static class DBC
{
public static Storage<AreaTableEntry> AreaTable { get; set; }
public static Storage<AchievementEntry> Achievement { get; set; }
public static Storage<AnimationDataEntry> AnimationData { get; set; }
public static Storage<BroadcastTextEntry> BroadcastText { get; set; }
public static Storage<CreatureEntry> Creature { get; set; }
public static Storage<CreatureDifficultyEntry> CreatureDifficulty { get; set; }
Expand Down Expand Up @@ -223,6 +224,11 @@ public static HashSet<int> GetPhaseGroups(ICollection<ushort> phases)
return phaseGroups;
}

public static uint GetEmptyAnimStateID()
{
return (uint)AnimationData.Count;
}

public static readonly Dictionary<uint, string> Zones = new Dictionary<uint, string>();
public static readonly Dictionary<int, int> MapSpawnMaskStores = new Dictionary<int, int>();
public static readonly Dictionary<int, List<int>> MapDifficultyStores = new Dictionary<int, List<int>>();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using DBFileReaderLib.Attributes;

namespace WowPacketParser.DBC.Structures.BattleForAzeroth
{
[DBFile("AnimationData")]
public sealed class AnimationDataEntry
{
[Index(true)]
public uint ID;
public ushort Fallback;
public byte BehaviorTier;
public int BehaviorID;
[Cardinality(2)]
public int[] Flags = new int[2];
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using DBFileReaderLib.Attributes;

namespace WowPacketParser.DBC.Structures.CataclysmClassic
{
[DBFile("AnimationData")]
public sealed class AnimationDataEntry
{
[Index(true)]
public uint ID;
public ushort Fallback;
public byte BehaviorTier;
public short BehaviorID;
[Cardinality(2)]
public int[] Flags = new int[2];
}
}
16 changes: 16 additions & 0 deletions WowPacketParser/DBC/Structures/Dragonflight/AnimationDataEntry.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using DBFileReaderLib.Attributes;

namespace WowPacketParser.DBC.Structures.Dragonflight
{
[DBFile("AnimationData")]
public sealed class AnimationDataEntry
{
[Index(true)]
public uint ID;
public ushort Fallback;
public byte BehaviorTier;
public int BehaviorID;
[Cardinality(2)]
public int[] Flags = new int[2];
}
}
15 changes: 15 additions & 0 deletions WowPacketParser/DBC/Structures/Legion/AnimationDataEntry.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using DBFileReaderLib.Attributes;

namespace WowPacketParser.DBC.Structures.Legion
{
[DBFile("AnimationData")]
public sealed class AnimationDataEntry
{
[Index(true)]
public uint ID;
public int Flags;
public ushort Fallback;
public ushort BehaviorID;
public byte BehaviorTier;
}
}
16 changes: 16 additions & 0 deletions WowPacketParser/DBC/Structures/Shadowlands/AnimationDataEntry.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using DBFileReaderLib.Attributes;

namespace WowPacketParser.DBC.Structures.Shadowlands
{
[DBFile("AnimationData")]
public sealed class AnimationDataEntry
{
[Index(true)]
public uint ID;
public ushort Fallback;
public byte BehaviorTier;
public int BehaviorID;
[Cardinality(2)]
public int[] Flags = new int[2];
}
}
16 changes: 16 additions & 0 deletions WowPacketParser/DBC/Structures/TheWarWithin/AnimationDataEntry.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using DBFileReaderLib.Attributes;

namespace WowPacketParser.DBC.Structures.TheWarWithin
{
[DBFile("AnimationData")]
public sealed class AnimationDataEntry
{
[Index(true)]
public uint ID;
public ushort Fallback;
public byte BehaviorTier;
public short BehaviorID;
[Cardinality(2)]
public int[] Flags = new int[2];
}
}
22 changes: 22 additions & 0 deletions WowPacketParser/Parsing/Parsers/QuestHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,28 @@ public static void AddQuestEnder(WowGuid questgiverGUID, uint questID)
}
}

public static void AddSpawnTrackingData(QuestPOI questPoi, TimeSpan TimeSpan)
{
// spawn_tracking_quest_objective
if (Storage.QuestObjectives.ContainsKey((uint)questPoi.QuestObjectiveID))
{
if (questPoi.SpawnTrackingID != 0 && questPoi.QuestObjectiveID != 0)
{
SpawnTrackingQuestObjective spawnTrackingQuestObjective = new SpawnTrackingQuestObjective
{
SpawnTrackingId = (uint)questPoi.SpawnTrackingID,
QuestObjectiveId = (uint)questPoi.QuestObjectiveID
};

Storage.SpawnTrackingQuestObjectives.Add(spawnTrackingQuestObjective, TimeSpan);
}
}

// spawn_tracking_template (helper to retrieve the mapId)
if (questPoi.SpawnTrackingID != 0)
Storage.SpawnTrackingMaps.Add((uint)questPoi.SpawnTrackingID, (int)questPoi.MapID);
}

private static void ReadExtraQuestInfo510(Packet packet)
{
packet.ReadUInt32("Choice Item Count");
Expand Down
55 changes: 55 additions & 0 deletions WowPacketParser/SQL/Builders/QuestMisc.cs
Original file line number Diff line number Diff line change
Expand Up @@ -276,5 +276,60 @@ public static string GameObjectQuestEnders()
return $"{questName} ended by {gobName}";
});
}

[BuilderMethod]
public static string SpawnTrackingTemplates()
{
if (!Settings.SQLOutputFlag.HasAnyFlagBit(SQLOutput.quest_template))
return string.Empty;

if (Storage.SpawnTrackingTemplates.IsEmpty())
return string.Empty;

if (Settings.UseDBC)
{
foreach (var spawnTracking in Storage.SpawnTrackingTemplates)
{
if (Storage.SpawnTrackingMaps.TryGetValue((uint)spawnTracking.Item1.SpawnTrackingId, out int mapId) && DBC.DBC.Map.ContainsKey(mapId))
{
var map = DBC.DBC.Map[mapId];
while (map.ParentMapID != -1 || map.CosmeticParentMapID != -1)
{
int parentMapId = map.ParentMapID != -1 ? map.ParentMapID : map.CosmeticParentMapID;
if (!DBC.DBC.Map.ContainsKey(parentMapId))
break;

map = DBC.DBC.Map[parentMapId];
mapId = parentMapId;
}

spawnTracking.Item1.MapId = (uint)mapId;
}
}
}

var templatesDb = SQLDatabase.Get(Storage.SpawnTrackingTemplates);

return SQLUtil.Compare(Storage.SpawnTrackingTemplates, templatesDb, x =>
{
string phase = StoreGetters.GetName(StoreNameType.PhaseId, (int)x.PhaseId, true);
string map = StoreGetters.GetName(StoreNameType.Map, (int)x.MapId, true);
return $"Map: {map} - Phase: {phase}";
});
}

[BuilderMethod]
public static string SpawnTrackingQuestObjectives()
{
if (!Settings.SQLOutputFlag.HasAnyFlagBit(SQLOutput.quest_template))
return string.Empty;

if (Storage.SpawnTrackingQuestObjectives.IsEmpty())
return string.Empty;

var templatesDb = SQLDatabase.Get(Storage.SpawnTrackingQuestObjectives);

return SQLUtil.Compare(Storage.SpawnTrackingQuestObjectives, templatesDb, StoreNameType.QuestObjective);
}
}
}
102 changes: 102 additions & 0 deletions WowPacketParser/SQL/Builders/Spawns.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,70 @@ private static bool GetTransportMap(WoWObject @object, out int mapId)
return true;
}

private static void AddSpawnTrackingData(RowList<SpawnTracking> spawnTrackingRows, RowList<SpawnTrackingState> spawnTrackingStateRows, uint spawnCount, WoWObject worldObject)
{
var objectType = worldObject.Type;
if (objectType != ObjectType.Unit && objectType != ObjectType.GameObject)
return;

var spawnType = objectType == ObjectType.Unit ? "CGUID" : "OGUID";
StoreNameType storeNameType = objectType == ObjectType.Unit ? StoreNameType.Unit : StoreNameType.GameObject;
var unitData = objectType == ObjectType.Unit ? (worldObject as Unit).UnitData : null;
var gameObjectData = objectType == ObjectType.GameObject ? (worldObject as GameObject).GameObjectData : null;

// spawn_tracking
var spawnTrackingRow = new Row<SpawnTracking>();
spawnTrackingRow.Data.SpawnId = $"@{spawnType}+{spawnCount}";
spawnTrackingRow.Data.SpawnType = (byte)(objectType == ObjectType.Unit ? 0 : 1);
spawnTrackingRow.Data.QuestObjectiveId = unitData is not null ? unitData.StateWorldEffectsQuestObjectiveID : gameObjectData.StateWorldEffectsQuestObjectiveID;
spawnTrackingRow.Comment += StoreGetters.GetName(storeNameType, (int)worldObject.ObjectData.EntryID, false);

spawnTrackingRows.Add(spawnTrackingRow);

// spawn_tracking_state
var spawnTrackingStateRow = new Row<SpawnTrackingState>();
spawnTrackingStateRow.Data.SpawnId = $"@{spawnType}+{spawnCount}";
spawnTrackingStateRow.Data.SpawnType = (byte)(objectType == ObjectType.Unit ? 0 : 1);
spawnTrackingStateRow.Data.Visible = true;
spawnTrackingStateRow.Data.StateSpellVisualId = unitData is not null ? unitData.StateSpellVisualID : gameObjectData.StateSpellVisualID;
spawnTrackingStateRow.Data.StateAnimId = unitData is not null ? unitData.StateAnimID : gameObjectData.SpawnTrackingStateAnimID;
spawnTrackingStateRow.Data.StateAnimKitId = unitData is not null ? unitData.StateAnimKitID : gameObjectData.SpawnTrackingStateAnimKitID;
spawnTrackingStateRow.Data.StateWorldEffects = null;

if (spawnTrackingStateRow.Data.StateSpellVisualId <= 0)
spawnTrackingStateRow.Data.StateSpellVisualId = null;

uint emptyAnimId = DBC.DBC.GetEmptyAnimStateID();
if (emptyAnimId == 0 || spawnTrackingStateRow.Data.StateAnimId == emptyAnimId)
spawnTrackingStateRow.Data.StateAnimId = null;

if (spawnTrackingStateRow.Data.StateAnimKitId <= 0)
spawnTrackingStateRow.Data.StateAnimKitId = null;

string stateWorldEffects = string.Empty;
uint?[] stateWorldEffectIDs = unitData is not null ? unitData.StateWorldEffectIDs : gameObjectData.StateWorldEffectIDs;
if (stateWorldEffectIDs != null && stateWorldEffectIDs.Length != 0)
{
foreach (uint? worldEffectId in stateWorldEffectIDs)
{
if (worldEffectId == 0)
continue;

stateWorldEffects += $"{worldEffectId} ";
}

stateWorldEffects = stateWorldEffects.TrimEnd(' ');
}
if (!string.IsNullOrEmpty(stateWorldEffects))
spawnTrackingStateRow.Data.StateWorldEffects = stateWorldEffects;

spawnTrackingStateRow.Comment += StoreGetters.GetName(storeNameType, (int)worldObject.ObjectData.EntryID, false);

if (spawnTrackingStateRow.Data.StateSpellVisualId != null || spawnTrackingStateRow.Data.StateAnimId != null
|| spawnTrackingStateRow.Data.StateAnimKitId != null || spawnTrackingStateRow.Data.StateWorldEffects != null)
spawnTrackingStateRows.Add(spawnTrackingStateRow);
}

[BuilderMethod(Units = true)]
public static string Creature(Dictionary<WowGuid, Unit> units)
{
Expand All @@ -64,6 +128,8 @@ public static string Creature(Dictionary<WowGuid, Unit> units)
var dbFields = SQLUtil.GetDBFields<CreatureAddon>(false);
var rows = new RowList<Creature>();
var addonRows = new RowList<CreatureAddon>();
var spawnTrackingRows = new RowList<SpawnTracking>();
var spawnTrackingStateRows = new RowList<SpawnTrackingState>();

var unitList = Settings.SkipDuplicateSpawns
? units.Values.GroupBy(u => u, new SpawnComparer()).Select(x => x.First())
Expand Down Expand Up @@ -273,6 +339,9 @@ public static string Creature(Dictionary<WowGuid, Unit> units)
}
}

if (ClientVersion.AddedInVersion(ClientType.WarlordsOfDraenor) && creature.UnitData.StateWorldEffectsQuestObjectiveID > 0)
AddSpawnTrackingData(spawnTrackingRows, spawnTrackingStateRows, count, creature);

if (creature.Guid.GetHighType() == HighGuidType.Pet || (creature.IsTemporarySpawn() && !Settings.SaveTempSpawns))
{
row.CommentOut = true;
Expand Down Expand Up @@ -332,6 +401,20 @@ public static string Creature(Dictionary<WowGuid, Unit> units)
result.Append(addonSql.Build());
}

if (ClientVersion.AddedInVersion(ClientType.WarlordsOfDraenor) && Settings.SQLOutputFlag.HasAnyFlagBit(SQLOutput.quest_template))
{
result.Append(Environment.NewLine);
var spawnTrackingDelete = new SQLDelete<SpawnTracking>(spawnTrackingRows);
result.Append(spawnTrackingDelete.Build());
var spawnTrackingSql = new SQLInsert<SpawnTracking>(spawnTrackingRows, false);
result.Append(spawnTrackingSql.Build());
result.Append(Environment.NewLine);
var spawnTrackingStateDelete = new SQLDelete<SpawnTrackingState>(spawnTrackingStateRows);
result.Append(spawnTrackingStateDelete.Build());
var spawnTrackingStateSql = new SQLInsert<SpawnTrackingState>(spawnTrackingStateRows, false);
result.Append(spawnTrackingStateSql.Build());
}

return result.ToString();
}

Expand All @@ -355,6 +438,8 @@ public static string GameObject(Dictionary<WowGuid, GameObject> gameObjects)
uint count = 0;
var rows = new RowList<GameObjectModel>();
var addonRows = new RowList<GameObjectAddon>();
var spawnTrackingRows = new RowList<SpawnTracking>();
var spawnTrackingStateRows = new RowList<SpawnTrackingState>();

var gobList = Settings.SkipDuplicateSpawns
? gameObjects.Values.GroupBy(g => g, new SpawnComparer()).Select(x => x.First())
Expand Down Expand Up @@ -504,6 +589,9 @@ public static string GameObject(Dictionary<WowGuid, GameObject> gameObjects)
addonRows.Add(addonRow);
}

if (ClientVersion.AddedInVersion(ClientType.WarlordsOfDraenor) && go.GameObjectData.StateWorldEffectsQuestObjectiveID > 0)
AddSpawnTrackingData(spawnTrackingRows, spawnTrackingStateRows, count, go);

row.Data.SpawnTimeSecs = go.GetDefaultSpawnTime(go.DifficultyID ?? 0);
row.Data.AnimProgress = go.GameObjectData.PercentHealth;
row.Data.State = (uint)go.GameObjectData.State;
Expand Down Expand Up @@ -580,6 +668,20 @@ public static string GameObject(Dictionary<WowGuid, GameObject> gameObjects)
result.Append(addonSql.Build());
}

if (ClientVersion.AddedInVersion(ClientType.WarlordsOfDraenor) && Settings.SQLOutputFlag.HasAnyFlagBit(SQLOutput.quest_template))
{
result.Append(Environment.NewLine);
var spawnTrackingDelete = new SQLDelete<SpawnTracking>(spawnTrackingRows);
result.Append(spawnTrackingDelete.Build());
var spawnTrackingSql = new SQLInsert<SpawnTracking>(spawnTrackingRows, false);
result.Append(spawnTrackingSql.Build());
result.Append(Environment.NewLine);
var spawnTrackingStateDelete = new SQLDelete<SpawnTrackingState>(spawnTrackingStateRows);
result.Append(spawnTrackingStateDelete.Build());
var spawnTrackingStateSql = new SQLInsert<SpawnTrackingState>(spawnTrackingStateRows, false);
result.Append(spawnTrackingStateSql.Build());
}

return result.ToString();
}
}
Expand Down
4 changes: 2 additions & 2 deletions WowPacketParser/SQL/Builders/WDBTemplates.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,9 @@ public static string QuestObjective()
if (Storage.QuestObjectives.IsEmpty())
return string.Empty;

var templatesDb = SQLDatabase.Get(Storage.QuestObjectives);
var templatesDb = SQLDatabase.Get(Storage.QuestObjectives.Values);

return SQLUtil.Compare(Storage.QuestObjectives, templatesDb, StoreNameType.QuestObjective);
return SQLUtil.Compare(Storage.QuestObjectives.Values, templatesDb, StoreNameType.QuestObjective);
}

[BuilderMethod(true)]
Expand Down
21 changes: 21 additions & 0 deletions WowPacketParser/Store/Objects/SpawnTracking.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
using WowPacketParser.Misc;
using WowPacketParser.SQL;

namespace WowPacketParser.Store.Objects
{
[DBTableName("spawn_tracking")]
public sealed record SpawnTracking : IDataModel
{
[DBFieldName("SpawnTrackingId", true)]
public uint? SpawnTrackingId;

[DBFieldName("SpawnType", true)]
public byte? SpawnType;

[DBFieldName("SpawnId", true, true)]
public string SpawnId;

[DBFieldName("QuestObjectiveId")]
public uint? QuestObjectiveId;
}
}
Loading

0 comments on commit 6440018

Please sign in to comment.