Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added basic SQL output for spawn tracking #927

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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