wow-midnight-server

Community-фиксы — детали (RU)

English

Все фиксы аддитивные, обратимые и размещены на пустых/кастомных ID-бандах, поэтому не конфликтуют со стоковой установкой TrinityCore. Файлы — в ../../sql/.


01 — Дед-лок интро Earthen (фантомный квест)

Файл: sql/01_quest_earthen_intro_fix.sql

Симптом: свежий персонаж Earthen (союзная раса) завершает первый квест интро «Пробуждение» на Isle of Dorn, после чего ни один NPC ничего не даёт — интро линейное и фазовое, игрок оказывается заперт.

Причина: цепочка 79200 «Who am I?» → 79201 «The Analysis Interface» → 83328 «The Analysis Interface». Квест 79201 — устаревший дубликат (то же название, что у 83328) без дающего и без принимающего — его нельзя ни взять, ни сдать. Но 83328 (бригадир Uzjax) требует завершения 79201 (PrevQuestID = 79201), поэтому цепочка встаёт намертво.

Фикс: обходим фантом → цепочка идёт 79200 → 83328 напрямую. Применяется вживую через reload quest_template (без рестарта).

Класс бага: реальный, но без дающего/принимающего квест, назначенный в PrevQuestID другого квеста, жёстко блокирует цепочку. Внимание: значения- маркеры PrevQuestID (999999, 99999, …) — это не этот баг: таких ID не существует, движок пропускает проверку. Блокируют только пререквизиты, которые существуют, но недостижимы.


02 — Объекты, блокирующие квесты

Файл: sql/02_quest_gameobject_unblock.sql

+28 gameobject_template и +89 спавнов gameobject (банда guid 8500000+) для объектов, нужных квестам, но отсутствовавших в базе — разблокирует ~10 квестов. DisplayID взяты аутентично; координаты сконвертированы из map% в мировые через UiMapAssignment для билда 68275, Z/ориентация — от ближайшего существующего спавна на той же карте. Нужен рестарт worldserver.


03 — Привязки инстансов легаси-рейдов

Файл: sql/03_raid_instance_bindings.sql

Привязывает 5 рейдов — Dragon Soul (967), End Time (938), Hour of Twilight (940), Well of Eternity (939), Throne of the Four Winds (754) — к их скомпилированному скрипту инстанса. Это включает лок-аут, завершение журнала DungeonEncounter и персистентность состояния боссов (трекинг/сброс).

Ограничение: эти скрипты инстансов — заглушки; они не добавляют боевой ИИ боссов (нет boss_*.cpp). Боссы остаются на дефолтном ИИ. Нужен рестарт.


04 — Кладбища Harandar

Файл: sql/04_harandar_graveyards.sql

12 world_safe_locs + 12 связок graveyard_zone + 12 Духов-целителей (банда guid существ 11000773+) для Harandar (карта 2694), одной из новейших зон, чтобы смерть/воскрешение там работали. Взято как самодостаточный аддитивный файл. Нужен рестарт.


05 — Праздники разошлись с реальным календарём

Файл: sql/05_world_events_realign.sql

Симптом: к 2026 году годовые праздники начинаются на 2-4 дня раньше, а Ярмарка Новолуния промахивается примерно на две недели.

Причина: TrinityCore хранит праздник как якорную дату плюс фиксированный occurence в минутах. Стоковая TDB заякорена в 2022-2023, а 525600 минут — это ровно 365 дней, без високосной четверти, поэтому даже праздники с фиксированной датой сползают примерно на день каждые четыре года. Хуже того, всё, что реальный календарь задаёт правилом (Ярмарка Новолуния — первое воскресенье месяца, Noblegarden следует за Пасхой, Лунный фестиваль — за лунным новым годом), от этого правила уходит полностью.

Исправление: переякорить каждый праздник на его ближайшее настоящее наступление, чтобы и последующие годы попадали верно. Файл делает бэкап таблицы перед изменением. Нужен рестартgame_event читается при старте.

Важно при проверке: событие активно, когда MOD(минут с начала, occurence) < length, а не когда «сейчас» попадает между start_time и end_time. Эти две колонки ограничивают всю повторяющуюся серию, а не одно наступление.


06 — Заблудившиеся спавны (NPC стоят не там, где должны)

Файл: sql/06_remove_stray_spawns.sql

Симптом: рейдовые боссы в стартовых зонах, ивентовый реквизит у ворот Штормграда без привязанного ивента и собственные тестовые существа Blizzard, разгуливающие по миру.

Причина: артефакт снифленных данных — клиент увидел существо где-то во время скриптовой сцены, а захват записал его как постоянного жителя.

Почему это важно: босс 90-го уровня у ворот Штормграда — это мгновенная смерть для всех, кто качается мимо. Боты-компаньоны из этого репозитория гибли именно об это.

Исправление: удаляет 30 таких спавнов. Файл сначала делает бэкап каждой удаляемой строки, поэтому откат — один INSERT. Нужен рестарт.

Как они были найдены: такие легаси-записи часто дают 404 во внешних базах, потому что их просто нет в актуальных данных retail. Более надёжный признак — соседи: окружение спавна выдаёт его настоящий дом.


07 — NPC, которые должны патрулировать, но стоят вкопанными

Файл: sql/07_npc_frozen_patrols.sql

Симптом: страж или бродячий моб не двигается вообще. Это не проблема навигации — существо даже не начинает движение.

Причина: creature.MovementType = 2 означает «идти по маршруту», а id маршрута берётся из creature_addon.PathId. Если у спавна нет строки addon, либо PathId = 0, либо PathId ссылается на несуществующий waypoint_path, то WaypointMovementGenerator::DoInitialize() не может загрузить путь и возвращает false. Генератор движения не инициализируется, и существо стоит всё время, пока загружен грид, каждый раз записывая в лог couldn't load path for ....

Масштаб: 3 924 из 10 993 waypoint-спавнов — примерно треть всех патрулирующих NPC в мире. Аномалия именно в строке спавна, а не в шаблоне: у 3 773 из них creature_template.MovementType равен 0 (idle).

Исправление: привести спавн в соответствие с реальностью — wander_distance > 0 становится случайным движением в этом радиусе, иначе idle. Ровно так TrinityCore сам чинит другие противоречивые сочетания MovementType/wander_distance при загрузке. Настоящие маршруты восстановить нельзя — данных о них в базе никогда не было. Нужен рестарт.


08 — Существа, которые не могут появиться вообще

Файл: sql/08_npc_missing_models.sql

Симптом: в логе повторяется Creature (Entry: N) has no model defined ... can't load. Существа нет в мире — его нельзя увидеть, взять в цель или убить.

Причина: существу нужна хотя бы одна строка в creature_template_model. У 889 заспавненных записей (1 908 спавнов) её не было.

Почему это важно: большинство из них — невидимые служебные NPC: счётчики kill-credit и «зайцы» для целей квестов. Они и должны быть невидимыми, но существовать обязаны: цель квеста, считающая убийства credit-NPC, никогда не выполнится, если этот NPC не может заспавниться.

Исправление: выдать каждой заспавненной записи без модели канонический невидимый displayId 11686 — ту же модель, что используют собственные невидимые сталкеры TrinityCore. Затрагиваются только реально заспавненные записи. Нужен рестарт.


09 — Спавны, которых не может быть, или стоящие в недостижимом месте

Файл: sql/09_npc_broken_spawns.sql

Четыре дефекта, у каждого свой бэкап:

Нужен рестарт.


10 — Ссылки в никуда

Файл: sql/10_npc_orphan_references.sql

Строки creature_addon, указывающие на несуществующий маршрут (387), строки addon для уже удалённого спавна (26) и формации, у которых пропал лидер или участник (у 237 строк пропал лидер, у 355 — участник, а уникальных строк 386, потому что у части пропали оба). Формация с отсутствующим лидером никогда не собирается, поэтому те, кто должен идти за ним строем, стоят каждый на своей точке спавна.

Запускать после 07 и 09 — 09 удаляет сломанные спавны, превращая их строки addon в сирот, которых затем собирает этот файл. Нужен рестарт.


hotfixes/01 — Восемь карт, где не спавнится вообще ничего

Файл: sql/hotfixes/01_map_difficulty_unlock.sql Применяется к базе hotfixes, а не к world.

Симптом: восемь карт полностью пусты в игре, а в логе тысячи раз повторяется Table \creature` has creature (GUID: N) that is not spawned in any difficulty, skipped.`

Причина: ObjectMgr::LoadCreatures строит для каждой карты набор допустимых сложностей из sMapDifficultyStore, а затем оставляет из spawnDifficulties только те значения, что входят в этот набор. Для этих карт пересечение пустое: шесть — снятые с эксплуатации сценарии, чьи записи MapDifficulty Blizzard удалила из клиентских данных, а у двух есть запись со сложностью, которой нет в строках спавнов. Со строками спавнов всё в порядке — карте просто некуда их спавнить.

Исправление: добавить по одной строке MapDifficulty на карту со сложностью 0. Хранилища DB2 читаются и из файла, и из базы: DB2Store::LoadFromDB запускает загрузчик дважды — для VerifiedBuild > 0 и для <= 0 — поэтому кастомная строка с VerifiedBuild 0 попадает в хранилище штатно, а не случайно.

Не используйте сложность 1. В Difficulty.db2 нет записи 0, поэтому GetDefaultMapDifficulty() отсеивает эти строки, и они не могут повлиять на то, как создаётся и масштабируется инстанс. Настоящая сложность выбираема и перебила бы родной тюнинг Blizzard для карты.

Возвращает: 1 218 спавнов существ и 1 073 гейм-объекта, включая Darkmaul Citadel — подземелье Exile’s Reach, где не спавнился ни один NPC. Клиентам ничего не рассылается: рассылка идёт из отдельной таблицы hotfix_data, которую этот файл не трогает. Нужен рестарт.


11 — Один и тот же NPC, стоящий сам в себе

Наводишь на квестодателя в Штормграде, а под ним стоит второй, точно такой же. Клик перебирает их по очереди; один может быть в бою или уже «застолблён», второй нет. Движок таблицу creature не чистит — строки накопились от повторных импортов пересекающихся наборов данных, и один и тот же спавн существует несколько раз под разными guid.

Измерено: 2 598 групп, удалено 3 403 лишних спавна.

Сложность не в поиске, а в том, что удалять нельзя. Группировка по одним координатам даёт 7 319 групп и 8 379 спавнов — и почти 5 000 из них здоровые: разные фазы (игроки на разных этапах сюжета видят в одной точке разных NPC), разные модель или экипировка (визуально разные существа) и группы, где часть записей входит в game_event_creature, spawn_group или pool_members, а часть нет (движок выбирает одну — это вариативность, а не дубль). Фикс требует совпадения всех тринадцати полей, определяющих то, что видит игрок, и группы со смешанной принадлежностью пропускает целиком.

Остаётся вожак строя, если он в группе есть, иначе — спавн с наименьшим guid; связанные строки creature_addon и creature_formations удаляются вместе со спавном, поэтому осиротевших ссылок не возникает.

Известное ограничение: spawnDifficulties — список через запятую, порядок элементов в нём не нормализован, а группировка сравнивает его как строку. Два одинаковых спавна с разным порядком в списке не находятся — 4 пары из 3 403 на нашей базе. По той же причине после отката часть возвращённых строк перестаёт группироваться с двойником, поэтому откат проверяйте по числу строк, а не пересчётом групп дублей. Требует перезапуска.


hotfixes/02 — Подземелья без столкновений: переопределение назначило им родителем Азерот

Лог заполняется строками Could not load VMAP name:City of Threads, id:2669, ... и повторяет их всё время работы сервера — 1 367 строк за один прогон длиной 2 ч 46 мин на нашем реалме: Приорат Священного Пламени, Город Нитей, Рассветный Разрушитель, Гнездовье и Долина Вечных Цветов. Внутри задетых плиток у движка нет ни проверки видимости, ни высоты, ни столкновений вообще.

Строка-переопределение DB2 заменяет запись целиком, а не сливается по полям. В hotfixes.map лежали 18 строк, пустых во всём кроме IDDirectory NULL, MapName NULL, WdtFileDataID 0 и, что и ломало всё, ParentMapID = 0 и CosmeticParentMapID = 0.

Ноль здесь не «нет родителя» — это −1. Ноль — это карта 0, Азерот. Движок считал Азерот родителем этих подземелий. Карта без собственной геометрии для плитки берёт родительскую и сопоставляет её со своим файлом индексов; взятая не у того родителя геометрия описывает другое место, числа записей расходятся, и плитка отвергается целиком.

Измерено на плитке 26_29 Города Нитей: собственный файл индексов карты содержит 2 494 записи, настоящий родитель Каз Алгар — 2 494, точное совпадение, а Азерот — 22. Все задетые плитки выглядят одинаково.

Этих строк не было в hotfix_data, то есть клиентам они не рассылались: вред был только серверный. После удаления движок читает настоящую запись из Map.db2.

Результат: 1 367 ошибок за прогон до и 0 после. Затрагиваются только строки, пустые во всех пяти полях, поэтому осознанное переопределение случайно не попадёт под удаление. Требует перезапуска.


Применение и откат

Применить всё: scripts/apply_fixes.sh (добавь HOTFIXES_DB=hotfixes, чтобы включить sql/hotfixes/). Каждый файл безопасно запускать дважды — бэкапы создаются только если их ещё нет, поэтому второй запуск не затрёт исходные значения — и каждый файл заканчивается блоком отката в комментариях. Всегда делай бэкап базы world перед применением.

Каждый файл из этого документа проверен так: копия реальной базы откатывалась в сломанное состояние, применялось исправление, проверялось что счётчик дефектов дошёл до нуля, файл применялся второй раз, а затем выполнялся описанный откат — и исходные числа возвращались в точности.