Port to WoW client 12.0.7 (build 68453)

Adapt CypherCore from 11.2.5.63796 to 12.0.7.68453 using TrinityCore as
byte-exact reference.

- Fase 1: build gate 68453 (build_info + build_auth_key SQL, keys from TC)
- Fase 2: Opcodes.cs remapped by name to 12.0.7 values
- Fase 3: 32 DB2 structs + HotfixDatabase.cs SELECTs realigned; fixed
  ReputationManager/SpellInfo refs
- Fase 4: UpdateFields ported (GameObject/Conversation/AreaTrigger/Player/
  Unit/ActivePlayer + nested VisibleItem/QuestLog/BitVectors/CraftingOrderItem/
  SkillInfo)
- Critical protocol fixes: CreateObject header +3 bits (Room/Decor/MeshObject);
  EntityFragment enum renumbered to 12.0.7 (was 11.2.5 values, breaking every
  object packet)
- New-entity foundation: TypeId/TypeMask/HighGuid.MeshObject, fragment tags
- MeshObject entity scaffold (Midnight features are wire-format only)

Builds clean (dotnet build -c Release, 0 errors).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
adevopg
2026-07-12 19:44:16 +02:00
parent c968aa3c39
commit f58dbf7756
23 changed files with 5858 additions and 3106 deletions
@@ -100,6 +100,7 @@ namespace Game.DataStorage
public string GameType;
public string ShortDescription;
public string LongDescription;
public sbyte InstanceType;
public int PvpType;
public byte MinLevel;
public byte MaxLevel;
+6 -2
View File
@@ -63,10 +63,10 @@ namespace Game.DataStorage
public sealed class CharacterLoadoutRecord
{
public uint Id;
public long RaceMask;
public sbyte ChrClassID;
public int Purpose;
public byte ItemContext;
public long RaceMask;
public bool IsForNewCharacter() { return Purpose == 9; }
}
@@ -216,7 +216,6 @@ namespace Game.DataStorage
public sealed class ChrCustomizationReqRecord
{
public uint Id;
public long RaceMask;
public string ReqSource;
public int Flags;
public int ClassMask;
@@ -225,6 +224,7 @@ namespace Game.DataStorage
public int QuestID;
public int OverrideArchive; // -1: allow any, otherwise must match OverrideArchive cvar
public uint ItemModifiedAppearanceID;
public long RaceMask;
public bool HasFlag(ChrCustomizationReqFlag chrCustomizationReqFlag) { return (Flags & (int)chrCustomizationReqFlag) != 0; }
}
@@ -382,6 +382,7 @@ namespace Game.DataStorage
public int OrderIndex;
public int RedirectContentTuningID;
public int RedirectFlag;
public int RedirectEnum;
public uint ParentContentTuningID;
}
@@ -639,6 +640,9 @@ namespace Game.DataStorage
public uint RechargingCycleDurationMS;
public float AccountTransferPercentage;
public byte OrderIndex;
public float RecraftReagentCountPercentage;
public byte OrderSource;
public int MCRCurrencyID;
public int[] Flags = new int[2];
public bool HasFlag(CurrencyTypesFlags currencyTypesFlags) { return (Flags[0] & (int)currencyTypesFlags) != 0; }
+1 -1
View File
@@ -6,7 +6,6 @@ namespace Game.DataStorage
public sealed class EmotesRecord
{
public uint Id;
public long RaceMask;
public string EmoteSlashCommand;
public short AnimID;
public int EmoteFlags;
@@ -15,6 +14,7 @@ namespace Game.DataStorage
public uint EventSoundID;
public uint SpellVisualKitId;
public int ClassMask;
public long RaceMask;
}
public sealed class EmotesTextRecord
+7 -1
View File
@@ -8,7 +8,6 @@ namespace Game.DataStorage
public sealed class FactionRecord
{
public uint Id;
public long[] ReputationRaceMask = new long[4];
public LocalizedString Name;
public string Description;
public short ReputationIndex;
@@ -25,8 +24,15 @@ namespace Game.DataStorage
public int[] ReputationMax = new int[4];
public float[] ParentFactionMod = new float[2]; // Faction outputs rep * ParentFactionModOut as spillover reputation
public byte[] ParentFactionCap = new byte[2]; // The highest rank the faction will profit from incoming spillover
public long ReputationRaceMask1;
public long ReputationRaceMask2;
public long ReputationRaceMask3;
public long ReputationRaceMask4;
// helpers
// 12.0.7: client stores ReputationRaceMask as 4 separate RaceMask<int32,2> columns; expose as array for existing code
public long[] ReputationRaceMask => new long[] { ReputationRaceMask1, ReputationRaceMask2, ReputationRaceMask3, ReputationRaceMask4 };
public bool CanHaveReputation()
{
return ReputationIndex >= 0;
+1 -1
View File
@@ -219,7 +219,6 @@ namespace Game.DataStorage
public uint HordeConstructObjID;
public uint AllianceConstructObjID;
public byte Flags;
public byte UiCategoryID;
public uint[] UpgradeRequirement = new uint[2];
}
@@ -332,6 +331,7 @@ namespace Game.DataStorage
public int TraitTreeID;
public int ProfessionID;
public int Unknown_1002_14;
public int NeighborhoodMapID;
public int SkillLineID;
}
+11 -3
View File
@@ -46,7 +46,11 @@ namespace Game.DataStorage
public uint ItemGroupSoundsID;
public int ContentTuningID;
public int ModifiedCraftingReagentItemID;
public byte Unknown1200;
public int CraftingQualityID;
public int ItemSquishEraID;
public float RecraftReagentCountPercentage;
public byte OrderSource;
}
public sealed class ItemAppearanceRecord
@@ -227,13 +231,15 @@ namespace Game.DataStorage
public ushort SpellCategoryID;
public int SpellID;
public ushort ChrSpecializationID;
public int PlayerConditionID;
}
public sealed class ItemExtendedCostRecord
{
public uint Id;
public ulong Money;
public ushort RequiredArenaRating;
public byte ArenaBracket; // arena slot restrictions (min slot value)
public byte ArenaBracket; // arena slot restrictions (min slot value)
public int Flags;
public byte MinFactionID;
public int MinReputation;
@@ -337,12 +343,12 @@ namespace Game.DataStorage
public int ItemLevel;
public int RequiredLevel;
public int Unknown1125;
public int Flags;
}
public sealed class ItemSearchNameRecord
{
public uint Id;
public long AllowableRace;
public string Display;
public byte OverallQualityID;
public int ExpansionID;
@@ -355,6 +361,7 @@ namespace Game.DataStorage
public uint RequiredAbility;
public ushort ItemLevel;
public int[] Flags = new int[5];
public long AllowableRace;
}
public sealed class ItemSetRecord
@@ -382,7 +389,6 @@ namespace Game.DataStorage
public sealed class ItemSparseRecord
{
public uint Id;
public long AllowableRace;
public string Description;
public string Display3;
public string Display2;
@@ -404,6 +410,7 @@ namespace Game.DataStorage
public uint MaxCount;
public uint MinReputation;
public uint RequiredAbility;
public long AllowableRace;
public uint SellPrice;
public uint BuyPrice;
public uint VendorStackCount;
@@ -416,6 +423,7 @@ namespace Game.DataStorage
public uint PlayerLevelToItemLevelCurveID;
public int ItemLevelOffsetCurveID;
public int ItemLevelOffsetItemLevel;
public int ItemSquishEraID;
public ushort ItemNameDescriptionID;
public ushort RequiredTransmogHoliday;
public ushort RequiredHoliday;
+2 -2
View File
@@ -94,8 +94,8 @@ namespace Game.DataStorage
public byte MaterialID;
public int MinimapStaticCol;
public byte[] FrameCountTexture = new byte[6];
public int[] Color = new int[2];
public float[] Float = new float[18];
public int[] Color = new int[3];
public float[] Float = new float[38];
public uint[] Int = new uint[4];
public float[] Coefficient = new float[4];
}
+3 -1
View File
@@ -116,9 +116,10 @@ namespace Game.DataStorage
public uint Id;
public ushort MapID;
public int Flags;
public int MaxMedals;
public uint ExpansionLevel;
public int RequiredWorldStateID; // maybe?
public short[] CriteriaCount = new short[3];
public short[] CriteriaCount = new short[5];
public int[] FirstRewardQuestID = new int[6];
public int[] RewardQuestID = new int[6];
}
@@ -248,6 +249,7 @@ namespace Game.DataStorage
public sealed class MovieRecord
{
public uint Id;
public LocalizedString Summary;
public byte Volume;
public byte KeyID;
public uint AudioFileDataID;
+11 -4
View File
@@ -15,6 +15,7 @@ namespace Game.DataStorage
public byte Type;
public byte Flags;
public uint UiTextureKitID;
public uint UiScenarioDisplayInfoID;
}
public sealed class ScenarioStepRecord
@@ -59,6 +60,7 @@ namespace Game.DataStorage
{
public uint Id;
public string Name;
public int Flags;
public int Unknown915;
}
@@ -98,7 +100,6 @@ namespace Game.DataStorage
public sealed class SkillLineAbilityRecord
{
public long RaceMask;
public string AbilityVerb;
public string AbilityAllVerb;
public uint Id;
@@ -115,6 +116,7 @@ namespace Game.DataStorage
public short UniqueBit;
public short TradeSkillCategoryID;
public ushort SkillupSkillLineID;
public long RaceMask;
public bool HasFlag(SkillLineAbilityFlags skillLineAbilityFlags) { return (Flags & (int)skillLineAbilityFlags) != 0; }
}
@@ -130,13 +132,13 @@ namespace Game.DataStorage
public sealed class SkillRaceClassInfoRecord
{
public uint Id;
public long RaceMask;
public ushort SkillID;
public int ClassMask;
public int Flags;
public int Availability;
public sbyte MinLevel;
public ushort SkillTierID;
public long RaceMask;
public bool HasFlag(SkillRaceClassInfoFlags skillRaceClassInfoFlags) { return (Flags & (int)skillRaceClassInfoFlags) != 0; }
}
@@ -245,6 +247,7 @@ namespace Game.DataStorage
public byte DifficultyID;
public ushort Category;
public int DefenseType;
public int DiminishType;
public sbyte DispelType;
public sbyte Mechanic;
public int PreventionType;
@@ -291,6 +294,7 @@ namespace Game.DataStorage
public uint Id;
public int Duration;
public int MaxDuration;
public int DurationPerResource;
}
public sealed class SpellEffectRecord
@@ -320,6 +324,7 @@ namespace Game.DataStorage
public float GroupSizeBasePointsCoefficient;
public float EffectBasePoints;
public int ScalingClass;
public int TargetNodeGraph;
public int[] EffectMiscValue = new int[2];
public uint[] EffectRadiusIndex = new uint[2];
public FlagArray128 EffectSpellClassMask;
@@ -446,7 +451,7 @@ namespace Game.DataStorage
public sealed class SpellMiscRecord
{
public uint Id;
public int[] Attributes = new int[16];
public int[] Attributes = new int[17];
public byte DifficultyID;
public ushort CastingTimeIndex;
public ushort DurationIndex;
@@ -551,6 +556,8 @@ namespace Game.DataStorage
public int SpellID;
public ushort CurrencyTypesID;
public ushort CurrencyCount;
public int OverrideRecraftCurrencyCount;
public byte OrderSource;
}
public sealed class SpellScalingRecord
@@ -559,7 +566,6 @@ namespace Game.DataStorage
public uint SpellID;
public uint MinScalingLevel;
public uint MaxScalingLevel;
public ushort ScalesFromItemLevel;
}
public sealed class SpellShapeshiftRecord
@@ -614,6 +620,7 @@ namespace Game.DataStorage
public uint Id;
public float[] MissileCastOffset = new float[3];
public float[] MissileImpactOffset = new float[3];
public int StateKit;
public uint AnimEventSoundID;
public int Flags;
public sbyte MissileAttachment;
@@ -316,6 +316,7 @@ namespace Game.DataStorage
public sealed class TraitTreeRecord
{
public LocalizedString TitleText;
public uint Id;
public uint TraitSystemID;
public int Unused1000_1;
@@ -324,6 +325,7 @@ namespace Game.DataStorage
public int Flags;
public float Unused1000_2;
public float Unused1000_3;
public int UiTextureKitID;
public bool HasFlag(TraitTreeFlag traitTreeFlag) { return (Flags & (int)traitTreeFlag) != 0; }
}
@@ -16,7 +16,6 @@ namespace Game.DataStorage
public UiMapType Type;
public int BountySetID;
public uint BountyDisplayLocation;
public int VisibilityPlayerConditionID2; // if not met then map is skipped when evaluating UiMapAssignment
public int VisibilityPlayerConditionID; // if not met then client checks other maps with the same AlternateUiMapGroup, not re-evaluating UiMapAssignment for them
public sbyte HelpTextPosition;
public int BkgAtlasID;