# mod-taxi-builder GM-инструмент для создания собственных точек полёта и маршрутов прямо в игре на сервере MoonWell (AzerothCore 3.3.5a). Не требует ручного редактирования DBC: все данные складываются в три таблицы `acore_world`, а внешний CLI [moonwell-taxi](../../tools/moonwell-taxi/) собирает из них `TaxiNodes.dbc`, `TaxiPath.dbc` и `TaxiPathNode.dbc` для серверной `dbc/`-папки и клиентского патча. Все команды требуют права `SEC_ADMINISTRATOR` (стандартный GM-уровень 3+). --- ## Содержание - [Установка](#установка) - [Полный сценарий: новый маршрут с нуля](#полный-сценарий-новый-маршрут-с-нуля) - [Команды](#команды) - [`.taxi node`](#taxi-node) - [`.taxi path`](#taxi-path) - [`.taxi record`](#taxi-record) - [`.taxi markers`](#taxi-markers) - [`.taxi export dbc`](#taxi-export-dbc) - [Лимит TaxiMask и распределение id](#лимит-taximask-и-распределение-id) - [Правила валидации](#правила-валидации) - [Прореживание точек: `moonwell-taxi thin`](#прореживание-точек-moonwell-taxi-thin) - [Структура БД](#структура-бд) - [Связь с CLI moonwell-taxi](#связь-с-cli-moonwell-taxi) - [Известные ограничения](#известные-ограничения) - [FAQ / типичные ошибки](#faq--типичные-ошибки) --- ## Установка 1. Клонируйте репозиторий ядра и убедитесь, что папка `modules/mod-taxi-builder/` присутствует. 2. Примените миграцию схемы. Самый простой способ — через [import-custom-sql.sh](../../import-custom-sql.sh): ```bash docker compose up -d ac-database bash import-custom-sql.sh ``` В `acore_world` появятся три таблицы: `custom_taxi_nodes`, `custom_taxi_paths`, `custom_taxi_path_nodes`. Скрипт идемпотентен — повторный запуск пропустит уже созданные таблицы. 3. Соберите ядро со включенными модулями (значение по умолчанию): ```bash mkdir -p build && cd build cmake .. -DCMAKE_INSTALL_PREFIX=$HOME/azeroth-server \ -DCMAKE_BUILD_TYPE=RelWithDebInfo \ -DSCRIPTS=static -DMODULES=static make -j$(nproc) && make install ``` 4. Перезапустите worldserver. На старте появится строка о регистрации `taxi_builder_commandscript`. 5. (Один раз) поставьте `PyMySQL` для CLI: ```bash sudo apt install -y python3-pymysql ``` --- ## Полный сценарий: новый маршрут с нуля Допустим, GM хочет добавить кастомный маршрут «Златоземье → Штормград» (вернее, «новая точка → существующий узел Штормграда `id=2`»). ``` # 1. Встаём ровно туда, где должна быть новая точка полёта. .taxi node create "Златоземье" # -> [taxi-builder] node created: id=441 name="Златоземье" map=0 ... # 2. Создаём маршрут от новой точки к Штормграду (vanilla node id=2), # стоимость — 10 медяков. .taxi path create 441 2 10 # -> [taxi-builder] path created: id=1979 441 (Златоземье) -> 2 (Stormwind) ... # 3. Запускаем запись маршрута. .taxi record start 1979 # -> [taxi-builder] recording started for path 1979 (existing points: 0) # 4. Идём/летим по будущему маршруту. На каждой ключевой точке: .taxi record point # (или с комментарием — пригодится для последующего отчёта) .taxi record point "взлёт над таверной" .taxi record point .taxi record point # ...записываем 15-25 точек... # Если оступились — откат последней: .taxi record undo # 5. Завершаем запись. Точки сохраняются. .taxi record finish # -> path 1979 now has 18 waypoints. # 6. Проверяем маршрут на типовые проблемы. .taxi path validate 1979 # -> Validation: 0 errors, 2 warnings — OK # (Опционально) Расставить визуальные маркеры на всех точках, обойти их пешком: .taxi markers spawn 1979 # ... осмотр ... .taxi markers clear 1979 # 7. Из консоли (или из чата) запускаем экспорт DBC. moonwell-taxi export-dbc # 8. Кладём серверные DBC в папку сервера и перезапускаем worldserver. # 9. Кладём клиентские DBC в патч-MPQ и перезапускаем клиент (или удаляем Cache). # 10. На месте мастера полётов в Златоземье спавним NPC с npcflag=flight master # и фракцией Альянса/Орды (для MVP — стандартные команды AzerothCore .npc add). # 11. Подходим к нему, выбираем путь, летим. ``` После шага 9 точка появится у мастеров полётов и игроки смогут летать по записанному маршруту. --- ## Команды ### `.taxi node` Управление точками полёта (записи в `custom_taxi_nodes`). #### `.taxi node create "Name"` Создаёт новую такситочку из текущей позиции GM. Работает только из игры (нужны координаты). - `id` подбирается автоматически из безопасного диапазона (`>= 441`, избегая конфликтов с vanilla TaxiNodes ~313). - `mount_alliance` и `mount_horde` определяются автоматически: модуль смотрит, какие mount-creature чаще всего используются у vanilla точек на текущей карте, и подставляет их. Если карта неизвестна — fallback `541/541` (как в примере ТЗ). - Имя сохраняется как одна строка и при экспорте в DBC дублируется во все 16 локалей (отображается на любом клиенте). ``` .taxi node create "Златоземье" [taxi-builder] node created: id=441 name="Златоземье" map=0 xyz=(-9445.77, 79.40, 57.38) mounts=A:1572/H:295 ``` #### `.taxi node list [filter]` Печатает список точек: сначала custom (`custom_taxi_nodes`), затем vanilla (`sTaxiNodesStore`). Без аргумента дамп ограничен 50 строками (в чате не должно быть спама). С `filter` — подстрочный поиск без учёта регистра. Доступна и из консоли. ``` .taxi node list Storm [vanilla] 2 Stormwind, Elwynn @ map 0 (-9446.5, 64.4, 56.0) [vanilla] 38 Stonewatch Tower, Redridge @ map 0 (-9241.3, -2280.7, 65.0) Shown 2 (cap 50). Custom: 1, Vanilla: 365. ``` #### `.taxi node free` Печатает все свободные TaxiNode id в диапазоне `1..448`, сгруппированные в непрерывные подряд-идущие отрезки. Полезно перед `.taxi node create` посмотреть, сколько слотов осталось, и до создания убедиться, что auto-allocator не упрётся. Подробности — в разделе [Лимит TaxiMask и распределение id](#лимит-taximask-и-распределение-id). ```text .taxi node free Free TaxiNode ids in 1..448: 83 total (7 above-vanilla 441..448, 76 in vanilla gaps). 338..339 (2) ... 441..448 (8) ``` #### `.taxi node goto ` Телепортирует GM к такситочке (custom или vanilla — что найдётся первым). Удобно для быстрой проверки, что точка стоит, где надо. ```text .taxi node goto 441 [taxi-builder] teleported to custom node 441 (Златоземье) ``` --- ### `.taxi path` Управление маршрутами (записи в `custom_taxi_paths`). #### `.taxi path create ` Создаёт направленный маршрут между двумя taxi-нодами. Любой конец может быть как custom-нодом, так и vanilla — модуль ищет по обоим источникам и валит, если ID нигде нет. Если уже есть включённый путь с тем же `from→to` — отказ (нельзя случайно создать дубль). - `id` подбирается автоматически из диапазона `>= 1979`. - `cost` хранится в медяках. - `is_enabled` ставится в `1`. ``` .taxi path create 441 2 10 [taxi-builder] path created: id=1979 441 (Златоземье) -> 2 (Stormwind) cost=10 copper Now record waypoints with: .taxi record start 1979 -> .taxi record point ... -> .taxi record finish ``` #### `.taxi path reverse ` Создаёт обратный путь из существующего: 1. Делает новый `TaxiPath` `to → from` с тем же `cost`. 2. Копирует все `TaxiPathNode` исходного пути в обратном порядке, пересчитывая `node_index` от `0`. 3. Печатает предупреждение из ТЗ: ручную проверку посадки никто не отменял. ``` .taxi path reverse 1979 [taxi-builder] reversed path 1979 (441->2) into new path 1980 (2->441) with 18 waypoints. WARNING: reverse paths are auto-generated. Check landing waypoints by hand (`.taxi path goto 1980 `)... ``` #### `.taxi path list` Все custom-маршруты с резолвом имён узлов и счётчиком точек. ``` .taxi path list 1979 441 (Златоземье) -> 2 (Stormwind, Elwynn) cost=10 pts=18 enabled 1980 2 (Stormwind, Elwynn) -> 441 (Златоземье) cost=10 pts=18 enabled Total: 2 custom paths. ``` #### `.taxi path report ` Подробный отчёт по маршруту в формате секции 5.3 ТЗ. ``` .taxi path report 1979 PathID: 1979 (enabled) From: 441 Златоземье [custom] To: 2 Stormwind, Elwynn [vanilla] Cost: 10 copper Points: 18 Total distance: 1842.3 yd Min segment: 41.7 yd Max segment: 95.4 yd Last point distance to ToTaxiNode: 4.8 yd ``` #### `.taxi path validate ` Применяет правила секции 4 ТЗ. См. подробности в разделе [Правила валидации](#правила-валидации). ``` .taxi path validate 1979 [warn] segment 7->8 is 38.2 yd (< 40 yd, may be filtered by IsNodeIncludedInShortenedPath) [warn] sharp turn at NodeIndex 12: 89 deg (> 75). Consider adding intermediate points. Validation: 0 errors, 2 warnings — OK ``` При наличии errors статус — `FAIL`. Маршрут с errors не пропустит `.taxi path test`. #### `.taxi path goto ` Телепорт к конкретной точке маршрута. Удобно для шага «обход маркеров». ``` .taxi path goto 1979 7 [taxi-builder] teleported to path 1979, NodeIndex 7 map=0 (-9095.00, 230.00, 180.00) ``` #### `.taxi path test ` «Сухой запуск»: запускает валидацию (отказ при errors), затем телепортирует GM на первую точку маршрута, развернув лицом ко второй. Реальный полёт по таксиспрайнам в этой версии возможен только после прогона `moonwell-taxi export-dbc` и перезапуска worldserver+клиента. ``` .taxi path test 1979 [warn] segment 7->8 is 38.2 yd ... [taxi-builder] dropped at first waypoint of path 1979 (18 total). Step through with `.taxi path goto 1979 `. Real taxi flight needs `moonwell-taxi export-dbc` + worldserver/client restart. ``` --- ### `.taxi record` Запись точек маршрута. Запись — *сессия в памяти*, привязанная к GM-у: рестарт worldserver очистит сессию, но уже записанные waypoint-ы в БД останутся, и сессию можно продолжить через `.taxi record start `. #### `.taxi record start ` Начинает запись для существующего custom-пути. Запоминает baseline — число точек, которые были у пути до старта (для корректного `cancel`). ``` .taxi record start 1979 [taxi-builder] recording started for path 1979 (existing points: 0). Use `.taxi record point ["comment"]` and `.taxi record finish` when done. ``` #### `.taxi record point [comment]` Записывает следующую точку с координатами и map_id GM-а. `node_index` автоматически = `MAX(node_index) + 1` (или 0 для первой). Опциональный комментарий пишется в `comment` колонку (до 255 символов). ``` .taxi record point [taxi-builder] path=1979 point #0 map=0 (-9445.77, 79.40, 57.38) .taxi record point "разворот над озером" [taxi-builder] path=1979 point #1 map=0 (-9395.00, 80.00, 70.00) // разворот над озером ``` #### `.taxi record undo` Удаляет последнюю записанную в этой сессии точку. Не даёт уйти ниже baseline (точки, существовавшие до `start`, защищены). ``` .taxi record undo [taxi-builder] removed point #17 from path 1979. ``` #### `.taxi record finish` Закрывает сессию. Точки остаются в БД. Печатает счётчики. ``` .taxi record finish [taxi-builder] recording finished. Path 1979 now has 18 waypoints (added 18 this session). Run `.taxi path report 1979` or `.taxi path validate 1979` to review. ``` #### `.taxi record cancel` Откатывает текущую сессию: удаляет ровно те точки, которые добавил этот `start ... point ...`. Существовавшие до сессии waypoint-ы не трогаются — это полезно при правке уже готового маршрута. ``` .taxi record cancel [taxi-builder] recording cancelled for path 1979; rolled back to 5 waypoints. ``` --- ### `.taxi markers` Временные визуальные маркеры на waypoint-ах. По умолчанию используется GO `178559` (Banner Stand of Argent Crusade) — он мелкий, виден всем и есть в стоковой `gameobject_template`. При желании смените `MARKER_GO_ENTRY` в [src/cs_taxi_builder.cpp](src/cs_taxi_builder.cpp). #### `.taxi markers spawn ` Спавнит маркеры на всех точках маршрута, лежащих на текущей карте GM-а. Точки на других континентах пропускаются (грид всё равно не загружен — было бы бесполезно). Маркеры держатся 24 часа или до явного `clear`. Повторный `spawn` сначала чистит предыдущие, чтобы не плодить дубли. ``` .taxi markers spawn 1979 [taxi-builder] spawned 18 markers for path 1979 on map 0 (0 skipped on other maps). Auto-despawn in 86400 s. Use `.taxi markers clear 1979` to remove now. ``` #### `.taxi markers clear ` Удаляет все маркеры данного пути. ``` .taxi markers clear 1979 [taxi-builder] cleared 18 markers for path 1979. ``` --- ### `.taxi export dbc` Внутри игры команда не делает экспорт сама — она лишь напоминает запустить внешний CLI. Это сделано специально: генерация DBC, sha256 и копирование файлов — задача внешнего инструмента, чтобы не нагружать runtime worldserver-а. ``` .taxi export dbc [taxi-builder] export dbc stub: run `moonwell-taxi export-dbc` from CLI ``` См. раздел [Связь с CLI moonwell-taxi](#связь-с-cli-moonwell-taxi). --- ## Лимит TaxiMask и распределение id `TaxiNode.id` ограничен сверху значением **448**. Это hardcoded в `src/server/shared/DataStores/DBCStructure.h:2284`: ```cpp static constexpr std::size_t TaxiMaskSize = 14; typedef std::array TaxiMask; ``` 14 × 32 = 448 бит. По одному биту на каждую такситочку: «доступна ли в taxi-сети» и «открыл ли её игрок». Сервер в `ObjectMgr::GetNearestTaxiNode` пропускает любой узел с `(id - 1) / 32 >= 14`, то есть `id > 448`. Симптом нарушения: GM создал точку `id = 441`, custom NPC появился, но при выборе destination сервер берёт ближайший **vanilla** узел вместо нашего, строит другую трассу — игрок «крутится по дороге» и в конце сваливается. Видимых ошибок в логе нет. Поднять потолок без патча клиента нельзя: пакет `SMSG_SHOWTAXINODES` шлёт ровно 14 uint32, клиент 3.3.5a читает фиксированный размер. ### Что внутри 1..448 В реальной vanilla-DBC проекта используется до `id = 440`, и часть номеров в этом диапазоне пустует. Вместе с диапазоном выше vanilla (`441..448`) даёт ~84 свободных слота. Команда `.taxi node free` показывает текущую картину: ```text .taxi node free Free TaxiNode ids in 1..448: 84 total (8 above-vanilla 441..448, 76 in vanilla gaps). 338..339 (2) 343..347 (5) 349..351 (3) 354..356 (3) 360..374 (15) 378 385..391 (7) 396..403 (8) 406..437 (32) 441..448 (8) ``` `.taxi node create` использует двухзонный авто-выбор: 1. Сначала ищет первый свободный id в `441..448` (выше vanilla, всегда безопасно). 2. Если эта зона исчерпана — берёт первый свободный id в дырах `1..440`. «Свободный» означает: id не загружен в `sTaxiNodesStore` (merged DBC на сервере) **и** не присутствует в `custom_taxi_nodes` (может быть ещё не задеплоен в DBC). ### Что делать при исчерпании всех 84 слотов - Освободить слот: удалить лишнюю строку из `custom_taxi_nodes` (плюс зависимости в `custom_taxi_paths`). - Перейти на бинарный патч `Wow.exe` под расширенный `TaxiMaskSize` (Cataclysm-эра приватки делают так — увеличивают до 32 uint32 = 1024 ноды). Это вне scope модуля: нужен патч exe + изменение `TaxiMaskSize` в ядре + раскатка патча игрокам. - Использовать «фейковые» полёты через скриптового NPC и `MoveSplinePath`, без записи в TaxiMask. Не показывается на карте как обычная точка полёта, но id неограничены. Тоже отдельный модуль. --- ## Правила валидации `.taxi path validate ` применяет следующие правила (пороги — константы в [src/cs_taxi_builder.cpp](src/cs_taxi_builder.cpp), секция «Thresholds per spec section 4»): | # | Правило | Уровень | | --- | --- | --- | | 4.1 | `from`/`to`-нод существует (custom или vanilla) | error | | 4.1 | у пути есть хотя бы одна waypoint | error | | 4.1 | `node_index` идёт подряд от 0 | error | | 4.2 | минимум 3 точки | error | | 4.2 | рекомендация: 12+ точек | warning | | 4.3 | сегмент `> 200 yd` (XY) | error | | 4.3 | сегмент `> 120 yd` (XY) | warning | | 4.3 | сегмент `< 40 yd` (XY) | warning (`IsNodeIncludedInShortenedPath` режет) | | 4.3 | сегмент пересекает границу карты | error (полёт между континентами невозможен) | | 4.4 | первая точка `> 20 yd` от `FromTaxiNode` | warning | | 4.4 | последняя точка `> 20 yd` от `ToTaxiNode` | warning | | 4.4 | первая/последняя точка на другой карте, чем нод | error | | 4.5 | модуль абсолютной разницы Z последней точки и `ToTaxiNode > 30 yd` | warning (риск падения) | | 4.6 | поворот `> 75°` в плоскости XY между сегментами | warning | `.taxi path test` отказывается работать, если есть хотя бы один error. Warnings — индикативны, не блокируют. --- ## Прореживание точек: `moonwell-taxi thin` Самая частая поломка свежезаписанного маршрута: в полёте таксИ «крутится по дороге» и сбрасывает игрока на кладбище. Причина — большинство сегментов короче 40 yd. AzerothCore при построении spline-а вызывает `IsNodeIncludedInShortenedPath` (`SKIP_SPLINE_POINT_DISTANCE_SQ = 40 * 40`) и выкидывает waypoint-ы, которые ближе ~40 yd друг к другу. Если у вас 68 точек, среднее расстояние 12 yd — после фильтрации остаётся 3-5 точек, по которым spline крутится как угодно. Симптом виден прямо в `.taxi path validate `: ``` [warn] segment 19->20 is 12.1 yd (< 40 yd, may be filtered by IsNodeIncludedInShortenedPath) [warn] segment 20->21 is 12.7 yd (< 40 yd, ...) ... Validation: 0 errors, 67 warnings — OK ``` Чинить руками удалением точек по одной — больно. Внешний CLI имеет подкоманду `thin`, которая делает это безопасно: пишет бэкап, удаляет старые waypoint-ы и записывает прореженные с пересчитанным `node_index` от 0. ```bash # 1. посмотреть, что получится, без записи в БД python3 tools/moonwell-taxi/moonwell_taxi.py thin --path 1979 --min-yd 50 --dry-run # 2. реально применить python3 tools/moonwell-taxi/moonwell_taxi.py thin --path 1979 --min-yd 50 ``` Параметры: - `--path ` — обязательный, какой `custom_taxi_paths.id` чистить. - `--min-yd ` — минимальное XY-расстояние между соседними оставленными точками. Дефолт `50`. Для длинных прямых смело ставьте `60-80`, для извилистых — `40-50`. - `--dry-run` — показать план без модификации БД. Алгоритм: 1. Первая waypoint всегда сохраняется. 2. Каждая последующая сравнивается с последней оставленной по XY. Если ≥ `--min-yd` — сохраняется, иначе пропускается. 3. Последняя waypoint всегда сохраняется (анкер посадки). 4. Если предпоследняя получилась слишком близко к последней — её тоже выкидывают. Так не возникает «маленький хвост» на финише. 5. Все ID `node_index` пересчитываются от `0` без пропусков. 6. Перед удалением исходные строки копируются в `custom_taxi_path_nodes_backup__` — откатить можно прямым SQL (команда сама печатает rollback в конце). Пример вывода на боевом маршруте: ``` Path 1979: 68 -> 13 waypoints (min_yd=50.0, removed 55) Backup table: `custom_taxi_path_nodes_backup_1979_1778283612` To rollback: DELETE FROM custom_taxi_path_nodes WHERE path_id = 1979; INSERT INTO custom_taxi_path_nodes SELECT * FROM `custom_taxi_path_nodes_backup_1979_1778283612`; ``` Что делать после `thin`: ``` .taxi path validate 1979 # должно быть 0 errors, 0 warnings про <40 yd .taxi path report 1979 # Min/Max segment в коридоре 40-120 yd ``` Затем `bash tools/moonwell-taxi/redeploy.sh` — экспорт DBC, заливка в `var/client-data-live/dbc/`, рестарт worldserver-а. **Важно:** `thin` работает только с записями в `custom_taxi_path_nodes`. DBC сами по себе не меняются — для актуализации сервера нужен `redeploy.sh` (или ручной `export-dbc` + `cp` + `restart`). Без этого in-game проверка увидит старый, плотный маршрут. --- ## Структура БД Все три таблицы в `acore_world`. См. [data/sql/updates/pending_db_world/rev_*.sql](../../data/sql/updates/pending_db_world/). ``` custom_taxi_nodes id int unsigned PRIMARY KEY (модуль выдаёт >= 441) name varchar(100) (utf8mb4, утаивается во все 16 локалей DBC) map_id smallint unsigned position_x float position_y float position_z float mount_alliance int unsigned mount_horde int unsigned created_by bigint unsigned (GUID GM-а на момент создания) created_at timestamp updated_at timestamp custom_taxi_paths id int unsigned PRIMARY KEY (модуль выдаёт >= 1979) from_taxi_node_id int unsigned (custom ИЛИ vanilla) to_taxi_node_id int unsigned (custom ИЛИ vanilla) cost int unsigned (медяки) is_enabled tinyint(1) created_by, created_at, updated_at KEY idx_from_to (from_taxi_node_id, to_taxi_node_id) custom_taxi_path_nodes id int unsigned AUTO_INCREMENT (внутренний; DBC ID — на экспорте) path_id int unsigned node_index int unsigned (UNIQUE с path_id, 0-based) map_id smallint unsigned position_x, _y, _z float flags int unsigned (TaxiPathNode flags: teleport=1, stop=2 ...) delay int unsigned arrival_event_id int unsigned departure_event_id int unsigned comment varchar(255) created_at timestamp ``` ID-диапазоны: модуль предпочитает `441..448` для новых нод, а когда этот диапазон занят, использует свободные дырки внутри `1..440`. Для путей модуль начинает с `1979`, сразу после vanilla max `1978`. Схема БД эти правила не enforce-ит, поэтому в теории можно вставить ID руками, но дальше CLI откажется писать DBC при коллизии с vanilla. `from`/`to` в `custom_taxi_paths` намеренно без FK: маршрут регулярно ведёт от custom-нода к vanilla (как в исходном кейсе ТЗ — «Златоземье → Штормград»). --- ## Связь с CLI moonwell-taxi Модуль *только* пишет в БД. DBC генерирует внешний инструмент [tools/moonwell-taxi/](../../tools/moonwell-taxi/): ```bash # Прочесть custom_taxi_*, смерджить с vanilla DBC из корня репозитория, # записать оба комплекта (server + client) и проверить sha256. python3 tools/moonwell-taxi/moonwell_taxi.py export-dbc # Output: # build/taxi/export/server/dbc/TaxiNodes.dbc # build/taxi/export/server/dbc/TaxiPath.dbc # build/taxi/export/server/dbc/TaxiPathNode.dbc # build/taxi/export/client/DBFilesClient/TaxiNodes.dbc # build/taxi/export/client/DBFilesClient/TaxiPath.dbc # build/taxi/export/client/DBFilesClient/TaxiPathNode.dbc # + sha256.txt в каждом каталоге ``` Если sha256 серверной и клиентской копий не совпадают — CLI выходит с `exit code 1`, не считая экспорт валидным. **Важно:** модуль и CLI общаются *только через схему БД*. Никаких общих хедеров, FIFO или RPC. Меняется схема — обновляется и CLI. --- ## Известные ограничения - **Только custom_taxi_*, не модифицирует vanilla.** Чтобы поправить существующую такситочку или путь vanilla, нужны другие средства. - **Имя пишется в locale slot текущего vanilla DBC.** Exporter авто-инферит slot и `Name_lang_mask` из `TaxiNodes.dbc` (для ruRU это slot `8`). Per-locale override таблицы пока нет. - **Mount creature ID — авто-подбор по большинству на карте.** Для Outland/Northrend и нестандартных карт fallback `541/541` может выглядеть как Альянсный грифон у Орды. Меняется UPDATE-ом `custom_taxi_nodes.mount_horde`. - **`.taxi markers spawn` спавнит только на текущей карте.** Точки на других континентах пропускаются — таково ограничение grid-load. - **`.taxi path test` не запускает реальный flight.** Реальный полёт работает только через DBC; у `path test` MVP-уровень — телепорт на первую точку. Делать `Player::ActivateTaxiPathTo` для путей не из DBC требует более глубокой интеграции (out of scope MVP). - **Сессия записи не персистится.** Рестарт worldserver сбрасывает состояние `.taxi record`. Workaround: `.taxi record start ` заново — точки в БД остаются. - **Конвенция Alliance/Horde в DBC обратна тексту ТЗ.** В реальном DBC `MountCreatureID[0]` — Horde, `[1]` — Alliance (см. `ObjectMgr.cpp:7229`). Колонки `mount_alliance` / `mount_horde` в БД названы корректно — конверсия делается при записи в DBC. --- ## FAQ / типичные ошибки ### «Path 1979 has no waypoints — record some first» Создан путь, но не записано ни одной точки. Запусти `.taxi record start 1979`, пройди маршрут, нажимая `.taxi record point` в нужных местах, заверши `.taxi record finish`. ### «custom_taxi_paths id=N not found» Путь не существует или `is_enabled=0`. Список — `.taxi path list`. ### «from-node N does not exist (custom or vanilla)» ID не найден ни в `custom_taxi_nodes`, ни в vanilla `TaxiNodes.dbc`. Список — `.taxi node list `. ### «An enabled path X -> Y already exists with id Z» Активный маршрут `from→to` уже существует. Либо `UPDATE custom_taxi_paths SET is_enabled=0 WHERE id=Z;`, либо используй существующий маршрут. ### `.taxi path validate` показывает много `< 40 yd` warnings Сегменты слишком короткие — `IsNodeIncludedInShortenedPath` их режет и реальный полёт пройдёт «через» эти точки. Удали лишние через `.taxi record undo` или прямым `DELETE` по `node_index`. ### Marker GO entry 178559 is missing from gameobject_template На сервере зачищен banner GO. Поменяй `MARKER_GO_ENTRY` в [src/cs_taxi_builder.cpp](src/cs_taxi_builder.cpp) на любой видимый GO из `gameobject_template` и пересобери модуль. ### После `export-dbc` сервер видит точку, а клиент — нет Клиент читает свои `DBFilesClient/*.dbc` из MPQ-патчей. Проверь, что файлы из `build/taxi/export/client/DBFilesClient/` попали в подключённый патч (например, `patch-X.MPQ` с приоритетом выше базовых) и удалён клиентский `Cache/`. ### `Server/client DBC match: FAIL` после экспорта Не должно случаться при штатной работе CLI (server и client пишутся из одного буфера). Если всё-таки — баг в инструменте, открывайте issue с `sha256.txt` обоих комплектов.