From 54d145499e17c379c1cc552a759b88bd39e14e3e Mon Sep 17 00:00:00 2001 From: blinkysc <37940565+blinkysc@users.noreply.github.com> Date: Mon, 9 Feb 2026 20:07:23 -0600 Subject: [PATCH] fix(Core/GameObject): Handle zero quaternion rotation for dynamically spawned gameobjects (#24662) Co-authored-by: blinkysc Co-authored-by: zergtmn --- .../rev_1770672746227329103.sql | 30 +++++++++++++++++++ .../rev_1770676693634619814.sql | 13 ++++++++ .../game/Entities/GameObject/GameObject.cpp | 4 +++ .../game/Entities/Transport/Transport.cpp | 10 ++++--- src/server/game/Globals/ObjectMgr.cpp | 14 +++++++++ 5 files changed, 67 insertions(+), 4 deletions(-) create mode 100644 data/sql/updates/pending_db_world/rev_1770672746227329103.sql create mode 100644 data/sql/updates/pending_db_world/rev_1770676693634619814.sql diff --git a/data/sql/updates/pending_db_world/rev_1770672746227329103.sql b/data/sql/updates/pending_db_world/rev_1770672746227329103.sql new file mode 100644 index 000000000..e4ac075b6 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1770672746227329103.sql @@ -0,0 +1,30 @@ +-- Fix gameobject spawns with non-unit rotation quaternions (sniffed values) + +-- Barbershop Chairs +UPDATE `gameobject` SET `rotation0` = 0, `rotation1` = 0, `rotation2` = -0.66261959075927734, `rotation3` = 0.748956084251403808 WHERE `guid` = 4718; -- 190699 +UPDATE `gameobject` SET `rotation0` = 0, `rotation1` = 0, `rotation2` = -0.99496841430664062, `rotation3` = 0.100189015269279479 WHERE `guid` = 4958; -- 190697 +UPDATE `gameobject` SET `rotation0` = 0, `rotation1` = 0, `rotation2` = 0.83388519287109375, `rotation3` = 0.55193793773651123 WHERE `guid` = 5136; -- 190698 +UPDATE `gameobject` SET `rotation0` = 0, `rotation1` = 0, `rotation2` = -0.85035133361816406, `rotation3` = 0.52621537446975708 WHERE `guid` = 5496; -- 190704 + +-- Legends of the Earth (2657) +UPDATE `gameobject` SET `rotation0` = 0.177045822143554687, `rotation1` = -0.68458366394042968, `rotation2` = -0.66014003753662109, `rotation3` = 0.253407031297683715 WHERE `guid` = 12007; + +-- Battered Chest (106318) +UPDATE `gameobject` SET `rotation0` = 0, `rotation1` = 0, `rotation2` = 0.99965667724609375, `rotation3` = 0.026201646775007247 WHERE `guid` = 26916; +UPDATE `gameobject` SET `rotation0` = 0, `rotation1` = 0, `rotation2` = 0.6883544921875, `rotation3` = 0.725374460220336914 WHERE `guid` = 85745; +UPDATE `gameobject` SET `rotation0` = 0, `rotation1` = 0, `rotation2` = -0.19936752319335937, `rotation3` = 0.979924798011779785 WHERE `guid` = 85756; +UPDATE `gameobject` SET `rotation0` = 0, `rotation1` = 0, `rotation2` = 0.477158546447753906, `rotation3` = 0.878817260265350341 WHERE `guid` = 85879; + +-- Water Well Cleansing Aura (2904) +UPDATE `gameobject` SET `rotation0` = 0, `rotation1` = 0, `rotation2` = 0.374606132507324218, `rotation3` = 0.927184045314788818 WHERE `guid` = 46424; +UPDATE `gameobject` SET `rotation0` = 0, `rotation1` = 0, `rotation2` = 0.034898757934570312, `rotation3` = 0.999390840530395507 WHERE `guid` = 46425; +UPDATE `gameobject` SET `rotation0` = 0, `rotation1` = 0, `rotation2` = 0.99965667724609375, `rotation3` = 0.026201646775007247 WHERE `guid` = 46429; + +-- Frostwyrm Waterfall Door (181225, Naxxramas) +UPDATE `gameobject` SET `rotation0` = 0, `rotation1` = 0, `rotation2` = -0.77439212799072265, `rotation3` = 0.632705986499786376 WHERE `guid` = 67868; + +-- Axxarien Crystal (185056) +UPDATE `gameobject` SET `rotation0` = 0.045565605163574218, `rotation1` = 0.100982666015625, `rotation2` = 0.293343544006347656, `rotation3` = 0.949566125869750976 WHERE `guid` = 99796; + +-- Cage (185474, Serpentshrine Cavern) - normalized from (0, 0, 0.7, -0.7) +UPDATE `gameobject` SET `rotation0` = 0, `rotation1` = 0, `rotation2` = -0.70710678118654752, `rotation3` = 0.70710678118654752 WHERE `guid` = 265632; diff --git a/data/sql/updates/pending_db_world/rev_1770676693634619814.sql b/data/sql/updates/pending_db_world/rev_1770676693634619814.sql new file mode 100644 index 000000000..a7e101d5e --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1770676693634619814.sql @@ -0,0 +1,13 @@ +-- Add missing static transport parent_rotation values +DELETE FROM `gameobject_addon` WHERE `guid` IN (2837,6946,18802,18803,18804,18805,18806,18807,56162,56163); +INSERT INTO `gameobject_addon` (`guid`,`parent_rotation0`,`parent_rotation1`,`parent_rotation2`,`parent_rotation3`,`invisibilityType`,`invisibilityValue`) VALUES +(2837,0,0,0.996917,-0.078459,0,0), +(6946,0,0,0.992005,-0.126199,0,0), +(18802,0,0,1,0,0,0), +(18803,0,0,1,0,0,0), +(18804,0,0,1,0,0,0), +(18805,0,0,1,0,0,0), +(18806,0,0,1,0,0,0), +(18807,0,0,1,0,0,0), +(56162,0,0,1,-4.37114e-08,0,0), +(56163,0,0,1,-4.37114e-08,0,0); diff --git a/src/server/game/Entities/GameObject/GameObject.cpp b/src/server/game/Entities/GameObject/GameObject.cpp index fd6049057..7979eeb21 100644 --- a/src/server/game/Entities/GameObject/GameObject.cpp +++ b/src/server/game/Entities/GameObject/GameObject.cpp @@ -2209,6 +2209,10 @@ void GameObject::UpdatePackedRotation() void GameObject::SetWorldRotation(G3D::Quat const& rot) { G3D::Quat rotation = rot; + // If the quaternion is zero (e.g. dynamically spawned GOs with no rotation), + // fall back to computing rotation from orientation to avoid NaN from unitize() + if (G3D::fuzzyEq(rotation.magnitude(), 0.0f)) + rotation = G3D::Quat::fromAxisAngleRotation(G3D::Vector3::unitZ(), GetOrientation()); rotation.unitize(); WorldRotation = rotation; UpdatePackedRotation(); diff --git a/src/server/game/Entities/Transport/Transport.cpp b/src/server/game/Entities/Transport/Transport.cpp index dbe068d14..ec12d2572 100644 --- a/src/server/game/Entities/Transport/Transport.cpp +++ b/src/server/game/Entities/Transport/Transport.cpp @@ -766,11 +766,13 @@ bool StaticTransport::Create(ObjectGuid::LowType guidlow, uint32 name_id, Map* m return false; } - // pussywizard: temporarily calculate WorldRotation from orientation, do so until values in db are correct - //SetWorldRotation( /*for StaticTransport we need 2 rotation Quats in db for World- and Path- Rotation*/ ); SetWorldRotationAngles(NormalizeOrientation(GetOrientation()), 0.0f, 0.0f); - // pussywizard: PathRotation for StaticTransport (only StaticTransports have PathRotation) - SetTransportPathRotation(rotation.x, rotation.y, rotation.z, rotation.w); + + // Prefer gameobject_addon parent_rotation for path rotation, fall back to gameobject.rotation + if (GameObjectAddon const* addon = sObjectMgr->GetGameObjectAddon(GetSpawnId())) + SetTransportPathRotation(addon->ParentRotation.x, addon->ParentRotation.y, addon->ParentRotation.z, addon->ParentRotation.w); + else + SetTransportPathRotation(rotation.x, rotation.y, rotation.z, rotation.w); SetObjectScale(goinfo->size); diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index 70164e6d9..1d0b5bdfe 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -2888,6 +2888,13 @@ void ObjectMgr::LoadGameobjects() continue; } + if (fabs(data.rotation.x * data.rotation.x + data.rotation.y * data.rotation.y + + data.rotation.z * data.rotation.z + data.rotation.w * data.rotation.w - 1.0f) >= 1e-5f) + { + LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: {} Entry: {}) with invalid rotation quaternion (non-unit), defaulting to orientation on Z axis only", guid, data.id); + data.rotation = G3D::Quat(G3D::Matrix3::fromEulerAnglesZYX(data.orientation, 0.0f, 0.0f)); + } + if (!MapMgr::IsValidMapCoord(data.mapid, data.posX, data.posY, data.posZ, data.orientation)) { LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: {} Entry: {}) with invalid coordinates, skip", guid, data.id); @@ -3028,6 +3035,13 @@ GameObjectData const* ObjectMgr::LoadGameObjectDataFromDB(ObjectGuid::LowType sp return nullptr; } + if (fabs(goData.rotation.x * goData.rotation.x + goData.rotation.y * goData.rotation.y + + goData.rotation.z * goData.rotation.z + goData.rotation.w * goData.rotation.w - 1.0f) >= 1e-5f) + { + LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: {} Entry: {}) with invalid rotation quaternion (non-unit), defaulting to orientation on Z axis only", spawnId, entry); + goData.rotation = G3D::Quat(G3D::Matrix3::fromEulerAnglesZYX(goData.orientation, 0.0f, 0.0f)); + } + if (!MapMgr::IsValidMapCoord(goData.mapid, goData.posX, goData.posY, goData.posZ, goData.orientation)) { LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: {} Entry: {}) with invalid coordinates, skipped.", spawnId, entry);