34 KiB
mod-taxi-builder
GM-инструмент для создания собственных точек полёта и маршрутов
прямо в игре на сервере MoonWell (AzerothCore 3.3.5a). Не требует
ручного редактирования DBC: все данные складываются в три таблицы
acore_world, а внешний CLI moonwell-taxi
собирает из них TaxiNodes.dbc, TaxiPath.dbc и TaxiPathNode.dbc
для серверной dbc/-папки и клиентского патча.
Все команды требуют права SEC_ADMINISTRATOR (стандартный GM-уровень
3+).
Содержание
- Установка
- Полный сценарий: новый маршрут с нуля
- Команды
- Лимит TaxiMask и распределение id
- Правила валидации
- Прореживание точек:
moonwell-taxi thin - Структура БД
- Связь с CLI moonwell-taxi
- Известные ограничения
- FAQ / типичные ошибки
Установка
-
Клонируйте репозиторий ядра и убедитесь, что папка
modules/mod-taxi-builder/присутствует. -
Примените миграцию схемы. Самый простой способ — через import-custom-sql.sh:
docker compose up -d ac-database bash import-custom-sql.shВ
acore_worldпоявятся три таблицы:custom_taxi_nodes,custom_taxi_paths,custom_taxi_path_nodes. Скрипт идемпотентен — повторный запуск пропустит уже созданные таблицы. -
Соберите ядро со включенными модулями (значение по умолчанию):
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 -
Перезапустите worldserver. На старте появится строка о регистрации
taxi_builder_commandscript. -
(Один раз) поставьте
PyMySQLдля CLI: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 точек на текущей карте, и подставляет их. Если карта неизвестна — fallback541/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.
.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 <nodeId>
Телепортирует GM к такситочке (custom или vanilla — что найдётся первым). Удобно для быстрой проверки, что точка стоит, где надо.
.taxi node goto 441
[taxi-builder] teleported to custom node 441 (Златоземье)
.taxi path
Управление маршрутами (записи в custom_taxi_paths).
.taxi path create <fromNodeId> <toNodeId> <cost>
Создаёт направленный маршрут между двумя 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 <pathId>
Создаёт обратный путь из существующего:
- Делает новый
TaxiPathto → fromс тем жеcost. - Копирует все
TaxiPathNodeисходного пути в обратном порядке, пересчитываяnode_indexот0. - Печатает предупреждение из ТЗ: ручную проверку посадки никто не отменял.
.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 <lastIndex>`)...
.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 <pathId>
Подробный отчёт по маршруту в формате секции 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 <pathId>
Применяет правила секции 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 <pathId> <nodeIndex>
Телепорт к конкретной точке маршрута. Удобно для шага «обход маркеров».
.taxi path goto 1979 7
[taxi-builder] teleported to path 1979, NodeIndex 7 map=0 (-9095.00, 230.00, 180.00)
.taxi path test <pathId>
«Сухой запуск»: запускает валидацию (отказ при 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 <index>`.
Real taxi flight needs `moonwell-taxi export-dbc` + worldserver/client restart.
.taxi record
Запись точек маршрута. Запись — сессия в памяти, привязанная к GM-у:
рестарт worldserver очистит сессию, но уже записанные waypoint-ы в БД
останутся, и сессию можно продолжить через .taxi record start <pathId>.
.taxi record start <pathId>
Начинает запись для существующего 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.
.taxi markers spawn <pathId>
Спавнит маркеры на всех точках маршрута, лежащих на текущей карте 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 <pathId>
Удаляет все маркеры данного пути.
.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.
Лимит TaxiMask и распределение id
TaxiNode.id ограничен сверху значением 448. Это hardcoded в
src/server/shared/DataStores/DBCStructure.h:2284:
static constexpr std::size_t TaxiMaskSize = 14;
typedef std::array<uint32, TaxiMaskSize> 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
показывает текущую картину:
.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 использует двухзонный авто-выбор:
- Сначала ищет первый свободный id в
441..448(выше vanilla, всегда безопасно). - Если эта зона исчерпана — берёт первый свободный 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 <pathId> применяет следующие правила
(пороги — константы в 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 <pathId>:
[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.
# 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 <id>— обязательный, какойcustom_taxi_paths.idчистить.--min-yd <float>— минимальное XY-расстояние между соседними оставленными точками. Дефолт50. Для длинных прямых смело ставьте60-80, для извилистых —40-50.--dry-run— показать план без модификации БД.
Алгоритм:
- Первая waypoint всегда сохраняется.
- Каждая последующая сравнивается с последней оставленной по XY.
Если ≥
--min-yd— сохраняется, иначе пропускается. - Последняя waypoint всегда сохраняется (анкер посадки).
- Если предпоследняя получилась слишком близко к последней — её тоже выкидывают. Так не возникает «маленький хвост» на финише.
- Все ID
node_indexпересчитываются от0без пропусков. - Перед удалением исходные строки копируются в
custom_taxi_path_nodes_backup_<pathId>_<unix_ts>— откатить можно прямым 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.
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/:
# Прочесть 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 это slot8). 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 testMVP-уровень — телепорт на первую точку. ДелатьPlayer::ActivateTaxiPathToдля путей не из DBC требует более глубокой интеграции (out of scope MVP).- Сессия записи не персистится. Рестарт worldserver сбрасывает
состояние
.taxi record. Workaround:.taxi record start <pathId>заново — точки в БД остаются. - Конвенция 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 <substring>.
«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 на любой видимый
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 обоих комплектов.