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
+177
View File
@@ -0,0 +1,177 @@
// Copyright (c) CypherCore <http://github.com/CypherCore> All rights reserved.
// Licensed under the GNU GENERAL PUBLIC LICENSE. See LICENSE file in the project root for full license information.
using Framework.Constants;
using Game.Maps;
using Game.Networking;
using Game.Networking.Packets;
using System.Collections.Generic;
namespace Game.Entities
{
public class MeshObject : WorldObject
{
MeshObjectData m_meshObjectData;
Position _stationaryPosition = new();
public MeshObject(bool isWorldObject = false) : base(isWorldObject)
{
ObjectTypeMask |= TypeMask.MeshObject;
ObjectTypeId = TypeId.MeshObject;
m_updateFlag.Stationary = true;
m_updateFlag.MeshObject = true;
m_entityFragments.Add(EntityFragment.Tag_MeshObject, false);
m_meshObjectData = new();
_stationaryPosition = new();
}
public override void AddToWorld()
{
if (!IsInWorld)
{
GetMap().GetObjectsStore().Add(GetGUID(), this);
base.AddToWorld();
}
}
public override void RemoveFromWorld()
{
if (IsInWorld)
{
base.RemoveFromWorld();
GetMap().GetObjectsStore().Remove(GetGUID());
}
}
public static MeshObject CreateMeshObject(Map map, Position pos, ObjectGuid privateObjectOwner)
{
if (map == null)
return null;
ulong lowGuid = map.GenerateLowGuid(HighGuid.MeshObject);
MeshObject meshObject = new();
if (!meshObject.Create(lowGuid, map, pos, privateObjectOwner))
{
meshObject.Dispose();
return null;
}
return meshObject;
}
bool Create(ulong lowGuid, Map map, Position pos, ObjectGuid privateObjectOwner)
{
SetMap(map);
Relocate(pos);
RelocateStationaryPosition(pos);
SetPrivateObjectOwner(privateObjectOwner);
_Create(ObjectGuid.Create(HighGuid.MeshObject, GetMapId(), 0, lowGuid));
SetObjectScale(1.0f);
// Register the updateable mesh-object-data fragment (vendor-style updateable fragment).
m_entityFragments.Add(EntityFragment.FMeshObjectData_C, IsInWorld);
if (!GetMap().AddToMap(this))
return false;
return true;
}
public override void BuildValuesCreate(WorldPacket data, UpdateFieldFlag flags, Player target)
{
m_objectData.WriteCreate(data, flags, this, target);
if (m_meshObjectData != null)
{
if (EntityFragmentsHolder.IsIndirectFragment(EntityFragment.FMeshObjectData_C))
data.WriteUInt8(1); // IndirectFragmentActive: FMeshObjectData_C
m_meshObjectData.WriteCreate(data, flags, this, target);
}
}
public override void BuildValuesUpdate(WorldPacket data, UpdateFieldFlag flags, Player target)
{
if ((m_entityFragments.ContentsChangedMask & m_entityFragments.GetUpdateMaskFor(EntityFragment.CGObject)) != 0)
{
data.WriteUInt32(m_values.GetChangedObjectTypeMask());
if (m_values.HasChanged(TypeId.Object))
m_objectData.WriteUpdate(data, flags, this, target);
}
if (m_meshObjectData != null && (m_entityFragments.ContentsChangedMask & m_entityFragments.GetUpdateMaskFor(EntityFragment.FMeshObjectData_C)) != 0)
m_meshObjectData.WriteUpdate(data, flags, this, target);
}
void BuildValuesUpdateForPlayerWithMask(UpdateData data, UpdateMask requestedObjectMask, UpdateMask requestedMeshObjectMask, Player target)
{
UpdateFieldFlag flags = GetUpdateFieldFlagsFor(target);
UpdateMask valuesMask = new((int)TypeId.Max);
if (requestedObjectMask.IsAnySet())
valuesMask.Set((int)TypeId.Object);
WorldPacket buffer = new();
BuildEntityFragmentsForValuesUpdateForPlayerWithMask(buffer, flags);
buffer.WriteUInt32(valuesMask.GetBlock(0));
if (valuesMask[(int)TypeId.Object])
m_objectData.WriteUpdate(buffer, requestedObjectMask, true, this, target);
// MeshObjectData is an updateable fragment (FMeshObjectData_C), written after the values block.
if (m_meshObjectData != null && requestedMeshObjectMask.IsAnySet())
m_meshObjectData.WriteUpdate(buffer, requestedMeshObjectMask, true, this, target);
WorldPacket buffer1 = new();
buffer1.WriteUInt8((byte)UpdateType.Values);
buffer1.WritePackedGuid(GetGUID());
buffer1.WriteUInt32(buffer.GetSize());
buffer1.WriteBytes(buffer.GetData());
data.AddUpdateBlock(buffer1);
}
public override void ClearUpdateMask(bool remove)
{
m_values.ClearChangesMask(m_meshObjectData);
base.ClearUpdateMask(remove);
}
public override uint GetFaction() { return 0; }
public override Position GetStationaryPosition() { return _stationaryPosition; }
void RelocateStationaryPosition(Position pos) { _stationaryPosition.Relocate(pos); }
class ValuesUpdateForPlayerWithMaskSender
{
MeshObject Owner;
ObjectFieldData ObjectMask = new();
MeshObjectData MeshObjectMask = new();
public ValuesUpdateForPlayerWithMaskSender(MeshObject owner)
{
Owner = owner;
}
public void Invoke(Player player)
{
UpdateData udata = new(Owner.GetMapId());
Owner.BuildValuesUpdateForPlayerWithMask(udata, ObjectMask.GetUpdateMask(), MeshObjectMask.GetUpdateMask(), player);
udata.BuildPacket(out UpdateObject packet);
player.SendPacket(packet);
}
public static implicit operator IDoWork<Player>(ValuesUpdateForPlayerWithMaskSender obj) => obj.Invoke;
}
}
}
@@ -143,6 +143,7 @@ namespace Game.Entities
case HighGuid.Corpse:
case HighGuid.LootObject:
case HighGuid.SceneObject:
case HighGuid.MeshObject:
case HighGuid.Scenario:
case HighGuid.AIGroup:
case HighGuid.DynamicDoor:
@@ -345,6 +346,8 @@ namespace Game.Entities
return TypeId.AreaTrigger;
case HighGuid.SceneObject:
return TypeId.SceneObject;
case HighGuid.MeshObject:
return TypeId.MeshObject;
case HighGuid.Conversation:
return TypeId.Conversation;
default:
@@ -377,6 +380,7 @@ namespace Game.Entities
case HighGuid.Corpse:
case HighGuid.LootObject:
case HighGuid.SceneObject:
case HighGuid.MeshObject:
case HighGuid.Scenario:
case HighGuid.AIGroup:
case HighGuid.DynamicDoor:
@@ -605,6 +609,7 @@ namespace Game.Entities
SET_GUID_INFO(HighGuid.Corpse, FormatWorldObject, ParseWorldObject);
SET_GUID_INFO(HighGuid.LootObject, FormatWorldObject, ParseWorldObject);
SET_GUID_INFO(HighGuid.SceneObject, FormatWorldObject, ParseWorldObject);
SET_GUID_INFO(HighGuid.MeshObject, FormatWorldObject, ParseWorldObject);
SET_GUID_INFO(HighGuid.Scenario, FormatWorldObject, ParseWorldObject);
SET_GUID_INFO(HighGuid.AIGroup, FormatWorldObject, ParseWorldObject);
SET_GUID_INFO(HighGuid.DynamicDoor, FormatWorldObject, ParseWorldObject);
File diff suppressed because it is too large Load Diff
@@ -8,33 +8,52 @@ namespace Game.Entities
{
public enum EntityFragment
{
CGObject = 0, // UPDATEABLE, INDIRECT,
Tag_Item = 1, // TAG,
Tag_Container = 2, // TAG,
Tag_AzeriteEmpoweredItem = 3, // TAG,
Tag_AzeriteItem = 4, // TAG,
Tag_Unit = 5, // TAG,
Tag_Player = 6, // TAG,
Tag_GameObject = 7, // TAG,
Tag_DynamicObject = 8, // TAG,
Tag_Corpse = 9, // TAG,
Tag_AreaTrigger = 10, // TAG,
Tag_SceneObject = 11, // TAG,
Tag_Conversation = 12, // TAG,
Tag_AIGroup = 13, // TAG,
Tag_Scenario = 14, // TAG,
Tag_LootObject = 15, // TAG,
Tag_ActivePlayer = 16, // TAG,
Tag_ActiveClient_S = 17, // TAG,
Tag_ActiveObject_C = 18, // TAG,
Tag_VisibleObject_C = 19, // TAG,
Tag_UnitVehicle = 20, // TAG,
FEntityPosition = 112,
FEntityLocalMatrix = 113,
FEntityWorldMatrix = 114,
CActor = 115, // INDIRECT,
FVendor_C = 117, // UPDATEABLE, INDIRECT,
FMirroredObject_C = 119,
// 12.0.7 (build 68453): fragment IDs renumbered vs 11.2.5; these are written into every object create/update packet.
FEntityPosition = 1,
CGObject = 2, // UPDATEABLE, INDIRECT,
FTransportLink = 5,
FPlayerOwnershipLink = 13,
CActor = 15, // INDIRECT,
FVendor_C = 17, // UPDATEABLE,
FMirroredObject_C = 18,
FMeshObjectData_C = 19, // UPDATEABLE,
FHousingDecor_C = 20, // UPDATEABLE,
FHousingRoom_C = 21, // UPDATEABLE,
FHousingRoomComponentMesh_C = 22, // UPDATEABLE,
FHousingPlayerHouse_C = 23, // UPDATEABLE,
FJamHousingCornerstone_C = 27, // UPDATEABLE,
FHousingDecorActor_C = 28,
FNeighborhoodMirrorData_C = 30, // UPDATEABLE,
FMirroredPositionData_C = 31, // UPDATEABLE,
PlayerHouseInfoComponent_C = 32, // UPDATEABLE, INDIRECT,
FHousingStorage_C = 33, // UPDATEABLE,
FHousingFixture_C = 34, // UPDATEABLE,
PlayerInitiativeComponent_C = 37, // UPDATEABLE, INDIRECT,
Tag_Item = 200, // TAG,
Tag_Container = 201, // TAG,
Tag_AzeriteEmpoweredItem = 202, // TAG,
Tag_AzeriteItem = 203, // TAG,
Tag_Unit = 204, // TAG,
Tag_Player = 205, // TAG,
Tag_GameObject = 206, // TAG,
Tag_DynamicObject = 207, // TAG,
Tag_Corpse = 208, // TAG,
Tag_AreaTrigger = 209, // TAG,
Tag_SceneObject = 210, // TAG,
Tag_Conversation = 211, // TAG,
Tag_AIGroup = 212, // TAG,
Tag_Scenario = 213, // TAG,
Tag_LootObject = 214, // TAG,
Tag_ActivePlayer = 215, // TAG,
Tag_ActiveClient_S = 216, // TAG,
Tag_ActiveObject_C = 217, // TAG,
Tag_VisibleObject_C = 218, // TAG,
Tag_UnitVehicle = 219, // TAG,
Tag_HousingRoom = 220, // TAG,
Tag_MeshObject = 221, // TAG,
Tag_HouseExteriorPiece = 224, // TAG,
Tag_HouseExteriorRoot = 225, // TAG,
Tag_HousingDecorProxyGameObject = 226, // TAG,
End = 255,
}
+10 -1
View File
@@ -39,7 +39,7 @@ namespace Game.Entities
m_movementInfo = new MovementInfo();
m_updateFlag.Clear();
m_entityFragments.Add((int)EntityFragment.CGObject, false);
m_entityFragments.Add(EntityFragment.CGObject, false);
m_objectData = new ObjectFieldData();
@@ -313,6 +313,9 @@ namespace Game.Entities
data.WriteBit(flags.SceneObject);
data.WriteBit(flags.ActivePlayer);
data.WriteBit(flags.Conversation);
data.WriteBit(flags.Room); // 12.0.7: new create-header bit
data.WriteBit(flags.Decor); // 12.0.7: new create-header bit
data.WriteBit(flags.MeshObject); // 12.0.7: new create-header bit
data.FlushBits();
if (flags.MovementUpdate)
@@ -4047,6 +4050,9 @@ namespace Game.Entities
public bool SceneObject;
public bool ActivePlayer;
public bool Conversation;
public bool Room; // 12.0.7: new
public bool Decor; // 12.0.7: new
public bool MeshObject; // 12.0.7: new
public void Clear()
{
@@ -4067,6 +4073,9 @@ namespace Game.Entities
SceneObject = false;
ActivePlayer = false;
Conversation = false;
Room = false;
Decor = false;
MeshObject = false;
}
}