Wow.exe 3.3.5a (12340) · снято отладчиком x32dbg
# Игра намертво зависает в цикле включения модели разрушаемой постройки
Процесс: **Wow.exe, PID 31764**, «не отвечает» Карта поля боя (`0xBEA564`): **607, Берег Древних**
## Коротко
Главный поток не выходит из обработки пакета обновления объектов. Внутри он бесконечно перебирает список сущностей одной группы модели разрушаемой постройки: каждый шаг снимает сущность и привязывает её заново, и две сущности по очереди возвращаются в обработку. Всё в таблицах ниже снято с зависшего клиента, если не сказано иначе.
## Стек главного потока (TID 33036)
Первый срез. Сторонних модулей в стеке нет, только `wow.exe`. У остальных 160 потоков EIP в системных DLL, не в `wow.exe`.
```
0x007C6AB2 sub_7C6790 EIP
0x007C9AC6 sub_7C9AB0
0x007CA629 sub_7CA600 ┐
0x007CA781 sub_7CA600 │ рекурсивный обход BSP группы,
0x007CA7C4 sub_7CA600 │ 8 кадров в этом срезе
… ещё 5 кадров sub_7CA600 ┘
0x007CB2BE CMapObjGroup::GetFacesForLinking
0x007C1E48 CWorldMap::LinkIntersectMapObjDefGroup
0x007C26D9 CWorldMap::LinkIntersectMapObjDef
0x007C27FE CWorldMap::LinkIntersectMapObjDefs
0x007C2987 CWorldMap::LinkStaticEntity
0x007C2F06 CWorldMap::LinkStaticEntityMultiple2
0x007C2FD3 CWorldMap::LinkStaticEntity_0
0x007A1BE3 CWorldMap::UpdateEntity(0x3D247EC0)
0x00783AE3 World::ObjectSetRenderEnabled(0x1F52A9D0, 1) ebp 0x0486EAD8
0x0070B6B2 CGGameObject_C_Type_DestructibleBuilding::PostReenable
0x004D7204 ObjectMgr::UpdateObjectFromPacket (хвостовой цикл PostReenable)
0x004D7417 PH_SMSG_UPDATE_OBJECT
0x004D759C PH_SMSG_COMPRESSED_UPDATE_OBJECT
0x0063202E NetClient::ProcessData
0x006324CE NetClient::HandleData
0x00633568 sub_6334F0
0x00464BF3 RealmConnection::PollNet
0x0047DC4C _SynthesizePoll
0x0047F29A SchedulerThreadProc
0x0040B7D8 wow.exe+0xB7D8 → kernelbase → ntdll
```
Следующие срезы: EIP `0x007CA709` (`sub_7CA600`, ebp `0x0486E764`), затем `0x0078F3CE`. Нижняя часть стека, от `ObjectSetRenderEnabled` и ниже, во всех срезах та же.
## Точки останова: что завершается, а что нет
Аппаратные точки на главном потоке, ставились по очереди.
| Адрес | Место | Срабатывала |
| --- | --- | --- |
| 0x7C26D9 | возврат из `LinkIntersectMapObjDefGroup` | да, многократно |
| 0x7C27FE | возврат из `LinkIntersectMapObjDefs` | да |
| 0x783ADE | вызов `UpdateEntity` внутри `ObjectSetRenderEnabled` | да, многократно |
| 0x4D7202 | следующий вызов `PostReenable` в хвостовом цикле | нет |
| 0x63202E | возврат из обработчика пакета в `NetClient::ProcessData` | нет |
| 0x47DC4C | возврат из `RealmConnection::PollNet` | нет |
Привязка одной сущности к геометрии каждый раз завершается. Из `ObjectSetRenderEnabled`, а значит и из `PostReenable` и из обработки пакета, выхода нет.
## Регистры на 0x783ADE: что перебирает цикл
Код: `ebx` — группа модели, `edi` — сущность, которую сейчас перепривязывают, `esi` — сохранённая следующая связь в списке группы.
| Срабатывание | ebx (группа) | edi (сущность) | esi (следующая связь) |
| --- | --- | --- | --- |
| 1 | 0x2B70CD58 | 0x3D247EC0 | 0x2218EAF4 |
| 2 | 0x2B70CD58 | 0x3D245620 | 0x2218EAD8 |
| 3 | 0x2B70CD58 | 0x3D247EC0 | 0x2218EBF0 |
| 4 | 0x2B70CD58 | 0x3D245620 | 0x2218EA14 |
| 5 | 0x2B70CD58 | 0x3D247EC0 | 0x2218EAF4 |
Группа одна и та же, две сущности чередуются, а узлы связей каждый раз новые: после каждой перепривязки сущность получает свежую связь в том же списке, и перебор доходит до неё снова.
## Прочее из памяти
- Модель, которую включают: `0x1F52A9D0` (аргумент `ObjectSetRenderEnabled` в кадре ebp `0x0486EAD8`).
- Отрезок, по которому привязывалась сущность `0x3D247EC0` (кадр `LinkStaticEntityMultiple2`): от (1218.98, 72.79, 52.36) до (1218.98, 72.79, −947.80). Тот же X и Y, по Z на 1000 ярдов вниз — то есть сущность стоит в точке около (1218.98, 72.79).
- Второй отрезок, глубже в том же стеке: от (72.80, −60.67, 97.38) до (72.80, −60.67, −906.6).
## Почему цикл не кончается (по коду клиента)
Это прочитано в коде `wow.exe`, а не снято вживую, но объясняет таблицу выше.
- `ObjectSetRenderEnabled(def, 1)` (0x783A60): для каждой группы модели берёт следующую связь, освобождает текущую (`CMap::FreeBaseObjLink`) и вызывает `CWorldMap::UpdateEntity`.
- `UpdateEntity` привязывает сущность заново через `CMap::LinkObjectToMapObjDefGroup` (0x7C1FF0), а та вызывает `TSList::Link`, то есть вставку в **конец** списка. Если сущность снова попадает в ту же группу, её новая связь оказывается за сохранённой следующей, и при двух сущностях перебор никогда не доходит до конца списка.
- `ObjectSetRenderEnabled` вызывают только из кода разрушаемой постройки (тип объекта 33): `PostReenable`, `Disable`, `UpdateDestructibleState`, `UpdateDisplay`, `PostInit`.
## Не проверено
- Как в группе уже включённой модели оказались две сущности до вызова `PostReenable`. По коду клиента это возможно, если постройка вернулась из кэша (после OUT_OF_RANGE) с другим состоянием и в том же пакете после неё пришли существа. Вживую порядок пакетов не снимался.
- Какой это объект: в рядом стоящей сборке TrinityCore ворота Пурпурного аметиста стоят в (1214.68, 81.21, 53.41) — в 10 ярдах от сущности выше. Это сопоставление, а не чтение GUID.
- Сценарий для повторения (GM): `.gobject add 190727`, два `.npc add` на ворота, `.modify phase 2`, `.gobject set state <spawnId> 5 2`, в течение 2 минут `.modify phase 1`. Не запускался.
[свернуть]