Music is missing on the Northrend zeppelins/boats.
https://www.youtube.com/watch?v=ZoRLB02eDNg
I looked into it, seems to have something to do with WMOAreaTable.dbc, namely the ZoneMusic field (http://www.pxr.dk/wowdev/wiki/index.php?title=WMOAreaTable.dbc).
I'm guessing something is missing in the object updates sent, or when entering the transport.
Know the music on NR transports was working on Mangos back in 2010 would not know after that since server moved to tc and have never seen it work on tc.
If you can tell which exact mangos branch, I'll dig into it
not shure if true, but try https://github.com/cmangos/mangos-wotlk/commits/master, https://github.com/scriptdev2/scriptdev2
Here is some information on this.
The two SoundEntries.dbc records corresponding to the transport music have ID #s 14952 and 14953 for day and night, respectively.
ZoneMusic.dbc contains one entry with ID #446, internal name is "Zone-NorthrendTransport". It uses SoundEntries.dbc records #14952 and #14953.
There are four entries in WMOAreaTable.dbc which use ZoneMusic record #446.
Here they are listed as ID, WMORootID, WMOGroupID
:
41797 4950 22826
42446 5020 23209
42951 5100 -1
43403 5165 -1
In a retail sniff recorded on version 3.3.3a (build 11723), the Kamagua transport was introduced with display id #7636. This points (from GameObjectDisplayInfo.dbc) to the filename World\wmo\transports\Tuskarrboat\Transport_Tuskarr_ship.wmo
. Note that no other display ID points to that WMO, and it is the correct WMO.
When you open that .wmo from the MPQs (using the 3.3.5a client), the root ID is 5100. That corresponds to our WMOAreaTable.dbc entry #42951 which references ZoneMusic entry #446.
Therefore, the question is, why is this zone music not playing? To answer that, we must look to the client.
In the 3.3.5a client, SI3::SetZoneMusicID()
is at 0x4C8D80. The second parameter to that function is the ZoneMusic DBC entry id, which we want to be 446. The first parameter is 'zoneSoundType'. When other zone music is being played, that value is apparently always 0. There are two places in the client that call SI3::SetZoneMusicID()
with the first parameter = 0. Those functions are:
AreaListUpdateSounds (0x78E9A0)
sub_78EEB0
-- not sure what this one is yet
AreaListUpdateSounds
is called when you walk onto a static WMO. For example, when you walk onto the Kamagua dock, the call looks like this:
AreaListUpdateSounds(0x105EF7E8, 0x10606998, 0x124BE880, 0x124BE8B0, false)
Root WMO AreaTable ID: 43379 ZoneMusic ID: 0
Root WMO AreaTable ID: 43380 ZoneMusic ID: 0
The first two pointers are AreaTable entries (and I'm assuming therefore not related to what we're doing). The third and fourth pointers are to WMOAreaTable entries within memory for the WMO root and the WMO group respectively. In this case, there is no change in music due to the WMO, which is correct, as there is no special music for the dock.
However, when you walk onto the transport itself, the AreaListUpdateSounds
call looks like the following:
AreaListUpdateSounds(0x105EF7E8, 0x10606998, 0x0, 0x0, false)
This tells me that, for whatever reason, the game is failing to identify the WMOAreaTable entry/entries for the transport. To look at why this might be happening, we must look at the function which calls AreaListUpdateSounds
. There are two direct callers for this function, SI3::ForceUpdateZoneSounds
and Handler_SMSG_UPDATE_WORLD_STATE
. It is reasonable to assume (and I think I've verified this, but I'm not sure) that Handler_SMSG_UPDATE_WORLD_STATE
is not the one we are concerned with. It doesn't actually matter, though, since they both seemingly call AreaListUpdateSounds
the exact same way.
SI3::ForceUpdateZoneSounds
is at 0x78EC70. The relevant portion of that function looks like this:
WMOAreaTableEntry *adtWMOArea = NULL;
WMOAreaTableEntry *rootWMOArea = NULL;
v13 = World::QueryObjectInside((World *)world);
World::QueryMapObjAreaTable(world, &adtWMOArea, &rootWMOArea, (int)&dword_ADF330);
AreaListUpdateSounds(areaTable1, areaTable2, rootWMOArea, adtWMOArea, v13);
This is useful because it tells us that World::QueryMapObjAreaTable
should theoretically be finding the transports WMOAreaTable entries, but is not.
The code for World::QueryMapObjAreaTable
is rather trivial:
bool __cdecl World::QueryMapObjAreaTable(int a1, WMOAreaTableEntry **adtArea, WMOAreaTableEntry **rootArea, int a4)
{
bool result; // al@2
if ( a1 )
result = CMapEntity::QueryMapObjAreaTable(a1, adtArea, rootArea, (_DWORD *)a4);
else
result = FALSE;
return result;
}
The code for CMapEntity::QueryMapObjAreaTable
is more interesting, however:
char __thiscall CMapEntity::QueryMapObjAreaTable(int this, WMOAreaTableEntry **adtArea, WMOAreaTableEntry **rootArea, _DWORD *a4)
{
int v4; // eax@1
int i; // ebx@3
int v6; // eax@7
int v7; // ecx@8
CMapObjDef *v8; // esi@11
CMapObj *v9; // edi@11
CMapObjGroup *v10; // eax@11
WMOAreaTableEntry *v11; // eax@15
int v13; // [sp+Ch] [bp-4h]@1
v4 = *(_DWORD *)(this + 32);
v13 = this;
if ( !(v4 & 1) && v4 )
goto LABEL_4;
for ( i = 0; !(i & 1); i = v4 )
{
if ( !i )
break;
v6 = *(_DWORD *)(i + 8);
if ( *(_BYTE *)(v6 + 8) & 4 )
break;
v7 = *(_DWORD *)(v6 + 32);
if ( v7 & 1 || !v7 )
v7 = 0;
v8 = *(CMapObjDef **)(v7 + 8);
v9 = v8->mapObj;
v10 = CMapObj::GetGroup(v8->mapObj, *(_DWORD *)(v6 + 80), 0);
if ( v10 && (!(v10->gap0[48] & 8) || !(v8->base.maybeObjTypeFlags & 0x400)) )
{
*a4 = *(_DWORD *)&v10->gapEC[148];
*adtArea = SDBWMOAreaTableLookup(v9->smoHeader->wmoId, v8->nameSetId, *(_DWORD *)&v10->gapEC[148]);
v11 = SDBWMOAreaTableLookup(v9->smoHeader->wmoId, v8->nameSetId, -1);
*rootArea = v11;
if ( *adtArea && v11 )
return 1;
return 0;
}
v4 = sub_4B6670(v13 + 24, i);
LABEL_4:
;
}
return 0;
}
What is going on here, basically, is map objects are being iterated over. For those that match the criterion, the WMOAreaTable entry is requested for them (for both the root WMO and group WMO).
For this function to fail to find the WMOAreaTable entry, either that WMO is not in the list which is being iterated over here, or the criterion in the if statement is failing. Here is an execution log from a debugger logging the WMO IDs of those WMOs being iterated over:
007A1695 HARD: wmoID = 1313 (4883.)
007A1695 HARD: wmoID = 142B (5163.)
007A1695 HARD: wmoID = 13EC (5100.)
5100 is our WMO! So, it must be the if-statement which is false and we (I think) want it to be true. I have confirmed that CMapObj::GetGroup
returns non-NULL for WMO 5100. The value of v10->gap0[48] for WMO 5100 is 0x00800009, which when you AND with 0x08, the result is non-zero. Therefore, the last condition must be true for the overall expression to be true. Unfortunately (and perhaps obviously), this is not the case. v8->base.maybeObjTypeFlags = 0x00004408, which when you AND with 0x400, is non-zero also.
So, to fix this issue, I suspect that we either want to somehow change the 0x00800009 or the 0x00004408. But I'm not sure how to do that.
Here is a snippet of the transport's creation from a retail sniff:
[12] UpdateType: CreateObject1
[12] GUID: Full: 0x1FC0000000000022 Type: Transport Low: 34
[12] Object Type: GameObject (5)
[12] Update Flags: Transport, LowGuid, StationaryObject, GORotation (594)
[12] Stationary Position: X: 0 Y: 0 Z: 0
[12] O: -3.016637
[12] Low GUID: 34
[12] Transport unk timer: 3895604873
[12] GO Rotation: X: 0 Y: 0 Z: 0 W: 1
[12] OBJECT_FIELD_GUID: 34/4.764415E-44
[12] 1: 532676608/8.131516E-20
[12] OBJECT_FIELD_TYPE: 33/4.624285E-44
[12] OBJECT_FIELD_ENTRY: 188511/2.641602E-40
[12] OBJECT_FIELD_SCALE_X: 1065353216/1
[12] GAMEOBJECT_DISPLAYID: 7636/1.070032E-41
[12] GAMEOBJECT_FLAGS: 40/5.605194E-44
[12] GAMEOBJECT_PARENTROTATION + 3: 1065353216/1
[12] GAMEOBJECT_DYNAMIC: 3009085440/-5.098991E-08
[12] GAMEOBJECT_LEVEL: 502354/7.039479E-40
[12] GAMEOBJECT_BYTES_1: 4278193921/-1.702191E+38
I believe I have verified that we match this data correctly.
Note that when you go up the steps and onto the deck of the transport, the 0x00800009 changes to 0x00800809. This is because the WMO group file changes. For this WMO, there are three groups. When you extract the relevant files from the MPQs, the MOGN chunk of the root WMO file gives the following three group names:
turtle_transport
stairs
turtlepower
In the linked video, the music is playing on the deck of the ship, which is the turtle_transport
group. The 0x00800809 flags come directly from the MPQs, specifically the MOGP chunk of the corresponding group file. For more information, see here: https://wowdev.wiki/WMO/v17#MOGP_chunk
According to that site, the 0x08 check corresponds to a check for 'is outdoors'. It makes sense that the entire WMO is marked as outdoors. Therefore, only the 0x4408 flag check is a possible solution here.
In contrast to the Kamagua transport's 0x4408 flags, the Kamagua dock has flags 0x80:
[8:12:36 PM] MapObjDef: 0x1F5312D8 Bounds: 786.4124, -2941.603, 5.490095, 864.3042, -2865.535, 32.14449 Id: 2 Flags: 0x80 VMT: 0xA3FFA8
[8:12:36 PM] MapObj: 0x1F447D28
[8:12:36 PM] SMOHeader: 0x1633437C WMO ID: 4883
We must now try to determine the origin of the 0x4408 and 0x80 flags for the CMapObjDef instances. The CMapObjDef constructor in the 3.3.5a client is at 0x7B4350. Here is a screenshot of the call stack when this function is called for the Kamagua transport:
https://dl.dropboxusercontent.com/u/54589875/transport_mapobjdef_create.png
The clr.dll frame is because I am using a manged DLL to monitor what is going on within the process, and can be ignored.
I put an on-write-memory breakpoint for ecx+0x0C (the offset of the 'maybeObjTypeFlags' field). Below is a list of addresses which triggered the breakpoint (or more accurately, instructions immediately following the triggering of the breakpoint):
0x7D728F - constructor, initializing value to 0
0x7BF1DB
007B64FC - sets value to 0, called shortly after constructor
007B5D13
Here is a screenshot of the call stack when the Kamagua dock is initialized: https://dl.dropboxusercontent.com/u/54589875/dock02_mapobjdef_create.png
Here is a list of instructions following the same flag setting for this object:
007D728F - constructor, initializing value to 0
007BF4FC - sets value to 0, called shortly after constructor
6/23/16 - update - my strong suspicion is that the sound is triggered by SMSG_PLAY_MUSIC. Therefore I am shifting my focus to writing a sniffer for my Legion Beta account to see if I can see how it works there.
I tried sniffing the packets, but without the names to go with the opcodes it is really just a bunch of gibberish. However I tried searching the packet data for the numerical values listed above, and they were not present. More importantly than that, I gave myself the ability to drop packets before the client parses them. I enabled this, boarded the transport, and the music started anyway.
This confirms that the music is started (on the current Legion Beta client) by the client, and not from a server packet. Time to go back to finding the origin of the 0x4408 flags for the transport within the 3.3.5a client.
As best as I can tell, the following functions set the corresponding flags:
sub_7B4760 -> 0x4000
sub_7B5D00 -> 0x0080
CMapObjDef::UpdateMoved( (0x7B64F0) -> 0x0400
This means that we need to determine why CMapObjDef::UpdateMoved()
is called for this object, and if we can avoid it.
6/24/2016 - update - I have found that 0x0400 is the flag for a WMO which has been moved (as is the case for transports). Therefore, this is also correct. I am pretty sure at this point that this is a client bug.
It's working correctly on 4.3.4 and probably also on master.
Most helpful comment
Here is some information on this.
The two SoundEntries.dbc records corresponding to the transport music have ID #s 14952 and 14953 for day and night, respectively.
ZoneMusic.dbc contains one entry with ID #446, internal name is "Zone-NorthrendTransport". It uses SoundEntries.dbc records #14952 and #14953.
There are four entries in WMOAreaTable.dbc which use ZoneMusic record #446.
Here they are listed as
ID, WMORootID, WMOGroupID
:In a retail sniff recorded on version 3.3.3a (build 11723), the Kamagua transport was introduced with display id #7636. This points (from GameObjectDisplayInfo.dbc) to the filename
World\wmo\transports\Tuskarrboat\Transport_Tuskarr_ship.wmo
. Note that no other display ID points to that WMO, and it is the correct WMO.When you open that .wmo from the MPQs (using the 3.3.5a client), the root ID is 5100. That corresponds to our WMOAreaTable.dbc entry #42951 which references ZoneMusic entry #446.
Therefore, the question is, why is this zone music not playing? To answer that, we must look to the client.
In the 3.3.5a client,
SI3::SetZoneMusicID()
is at 0x4C8D80. The second parameter to that function is the ZoneMusic DBC entry id, which we want to be 446. The first parameter is 'zoneSoundType'. When other zone music is being played, that value is apparently always 0. There are two places in the client that callSI3::SetZoneMusicID()
with the first parameter = 0. Those functions are:AreaListUpdateSounds (0x78E9A0)
sub_78EEB0
-- not sure what this one is yetAreaListUpdateSounds
is called when you walk onto a static WMO. For example, when you walk onto the Kamagua dock, the call looks like this:The first two pointers are AreaTable entries (and I'm assuming therefore not related to what we're doing). The third and fourth pointers are to WMOAreaTable entries within memory for the WMO root and the WMO group respectively. In this case, there is no change in music due to the WMO, which is correct, as there is no special music for the dock.
However, when you walk onto the transport itself, the
AreaListUpdateSounds
call looks like the following:This tells me that, for whatever reason, the game is failing to identify the WMOAreaTable entry/entries for the transport. To look at why this might be happening, we must look at the function which calls
AreaListUpdateSounds
. There are two direct callers for this function,SI3::ForceUpdateZoneSounds
andHandler_SMSG_UPDATE_WORLD_STATE
. It is reasonable to assume (and I think I've verified this, but I'm not sure) thatHandler_SMSG_UPDATE_WORLD_STATE
is not the one we are concerned with. It doesn't actually matter, though, since they both seemingly callAreaListUpdateSounds
the exact same way.SI3::ForceUpdateZoneSounds
is at 0x78EC70. The relevant portion of that function looks like this:This is useful because it tells us that
World::QueryMapObjAreaTable
should theoretically be finding the transports WMOAreaTable entries, but is not.The code for
World::QueryMapObjAreaTable
is rather trivial:The code for
CMapEntity::QueryMapObjAreaTable
is more interesting, however:What is going on here, basically, is map objects are being iterated over. For those that match the criterion, the WMOAreaTable entry is requested for them (for both the root WMO and group WMO).
For this function to fail to find the WMOAreaTable entry, either that WMO is not in the list which is being iterated over here, or the criterion in the if statement is failing. Here is an execution log from a debugger logging the WMO IDs of those WMOs being iterated over:
5100 is our WMO! So, it must be the if-statement which is false and we (I think) want it to be true. I have confirmed that
CMapObj::GetGroup
returns non-NULL for WMO 5100. The value of v10->gap0[48] for WMO 5100 is 0x00800009, which when you AND with 0x08, the result is non-zero. Therefore, the last condition must be true for the overall expression to be true. Unfortunately (and perhaps obviously), this is not the case. v8->base.maybeObjTypeFlags = 0x00004408, which when you AND with 0x400, is non-zero also.So, to fix this issue, I suspect that we either want to somehow change the 0x00800009 or the 0x00004408. But I'm not sure how to do that.
Here is a snippet of the transport's creation from a retail sniff:
I believe I have verified that we match this data correctly.
Note that when you go up the steps and onto the deck of the transport, the 0x00800009 changes to 0x00800809. This is because the WMO group file changes. For this WMO, there are three groups. When you extract the relevant files from the MPQs, the MOGN chunk of the root WMO file gives the following three group names:
In the linked video, the music is playing on the deck of the ship, which is the
turtle_transport
group. The 0x00800809 flags come directly from the MPQs, specifically the MOGP chunk of the corresponding group file. For more information, see here: https://wowdev.wiki/WMO/v17#MOGP_chunkAccording to that site, the 0x08 check corresponds to a check for 'is outdoors'. It makes sense that the entire WMO is marked as outdoors. Therefore, only the 0x4408 flag check is a possible solution here.
In contrast to the Kamagua transport's 0x4408 flags, the Kamagua dock has flags 0x80:
We must now try to determine the origin of the 0x4408 and 0x80 flags for the CMapObjDef instances. The CMapObjDef constructor in the 3.3.5a client is at 0x7B4350. Here is a screenshot of the call stack when this function is called for the Kamagua transport:
https://dl.dropboxusercontent.com/u/54589875/transport_mapobjdef_create.png
The clr.dll frame is because I am using a manged DLL to monitor what is going on within the process, and can be ignored.
I put an on-write-memory breakpoint for ecx+0x0C (the offset of the 'maybeObjTypeFlags' field). Below is a list of addresses which triggered the breakpoint (or more accurately, instructions immediately following the triggering of the breakpoint):
0x7D728F - constructor, initializing value to 0
0x7BF1DB
007B64FC - sets value to 0, called shortly after constructor
007B5D13
Here is a screenshot of the call stack when the Kamagua dock is initialized: https://dl.dropboxusercontent.com/u/54589875/dock02_mapobjdef_create.png
Here is a list of instructions following the same flag setting for this object:
007D728F - constructor, initializing value to 0
007BF4FC - sets value to 0, called shortly after constructor
6/23/16 - update - my strong suspicion is that the sound is triggered by SMSG_PLAY_MUSIC. Therefore I am shifting my focus to writing a sniffer for my Legion Beta account to see if I can see how it works there.
I tried sniffing the packets, but without the names to go with the opcodes it is really just a bunch of gibberish. However I tried searching the packet data for the numerical values listed above, and they were not present. More importantly than that, I gave myself the ability to drop packets before the client parses them. I enabled this, boarded the transport, and the music started anyway.
This confirms that the music is started (on the current Legion Beta client) by the client, and not from a server packet. Time to go back to finding the origin of the 0x4408 flags for the transport within the 3.3.5a client.
As best as I can tell, the following functions set the corresponding flags:
This means that we need to determine why
CMapObjDef::UpdateMoved()
is called for this object, and if we can avoid it.6/24/2016 - update - I have found that 0x0400 is the flag for a WMO which has been moved (as is the case for transports). Therefore, this is also correct. I am pretty sure at this point that this is a client bug.