Skip to content

fix(yaml): эмиттер должен квотить скаляры с YAML-индикаторами в начале (#3273)#3277

Closed
kvirund wants to merge 2 commits into
masterfrom
fix/yaml-emitter-quote-indicators-3273
Closed

fix(yaml): эмиттер должен квотить скаляры с YAML-индикаторами в начале (#3273)#3277
kvirund wants to merge 2 commits into
masterfrom
fix/yaml-emitter-quote-indicators-3273

Conversation

@kvirund
Copy link
Copy Markdown
Collaborator

@kvirund kvirund commented May 16, 2026

Диагноз

Прод-мир после OLC-сохранения не грузится с

FATAL: 1 object(s) failed to load. Aborting.

Корень — Koi8rYamlEmitter::Value() в src/engine/db/yaml_world_data_source.cpp пропускал &, *, !, , в списке стартовых символов, требующих квотинга. Имена объектов с цветовыми кодами &Yкрепеньки&Wй пенёк&n ложились в ямл без кавычек:

nominative: &Yкрепеньки&Wй пенёк&n

При следующем boot'е yaml-cpp видит &Y как декларацию якоря и валится с cannot assign multiple anchors to the same node. Объект уходит в error_count, на первом таком фейле сервер падает.

Симптом не воспроизводился у нас при мердже PR'ов, потому что свежесконвертированный мир из tools/converter/convert_to_yaml.py чист — ruamel.yaml сам квотит лидирующие индикаторы (preserve_quotes=True). Бьёт только мир, который пересохранил OLC.

Что в PR

  1. refactor (1-й коммит): вынес Koi8rYamlEmitter в src/engine/db/koi8r_yaml_emitter.h без изменения поведения — нужно для unit-тестирования.
  2. fix + test (2-й коммит): добавил &, *, !, , в стартовые символы, требующие квотинга. Добавил tests/koi8r.yaml.emitter.cpp — save → reload round-trip через yaml-cpp для всех проблемных индикаторов и для конкретного кейса &Yfoo &Wbar&n из тикета.

Проверил, что без фикса три из новых тестов падают (ColorCodesAtStartRoundTrip, SingleLeadingAmpersandRoundTrip и round-trip с lead-&), с фиксом — все восемь зелёные.

Closes #3273

Test plan

  • ninja tests/tests собирается с -Dyaml=builtin
  • ./tests/tests --gtest_filter='Koi8rYamlEmitter.*' — 9 passed
  • ./tests/tests --gtest_filter='YamlSaveEncoding.*' — без регресса, 3 passed
  • Сбутить ямл-мир, oedit'ом сохранить объект с цветовым именем, перебутить — должен подняться без FATAL'а (нужен прод-мир, который пересохранили; локально воспроизвёл на /home/stribog/lib/world-аналоге)

Не закрыто этим PR

  • keywords: на exit'ах комнаты пишется напрямую через out << без Koi8rYamlEmitter::Value() (yaml_world_data_source.cpp:3097-3142). Если у двери ключевое слово начнётся с &, тот же баг. Маловероятно встретить, но как follow-up — пропустить и эти ветки через эмиттер.

kvirund added 2 commits May 16, 2026 18:20
Класс перенесён один в один в src/engine/db/koi8r_yaml_emitter.h без
изменения поведения, чтобы его можно было покрыть unit-тестами без
включения всего yaml_world_data_source.cpp.

Preparation для фикса #3273.
#3273)

Koi8rYamlEmitter::Value() пропускал `&`, `*`, `!` и `,` в списке
символов, требующих квотинга в начале значения. Из-за этого имена
объектов и мобов с цветовыми кодами вида `&Yкрепеньки&Wй пенёк&n`
сохранялись через OLC без кавычек:

    nominative: &Yкрепеньки&Wй пенёк&n

При следующей загрузке yaml-cpp интерпретировал `&Y`/`&W` как
декларации якорей на одном узле и валил boot фатальной ошибкой
`cannot assign multiple anchors to the same node`. Объект уходил в
error_count, и при первом таком фейле сервер аварийно завершался
(`FATAL: 1 object(s) failed to load. Aborting.`).

Симптом проявлялся только на ямл-мирах, которые в какой-то момент
ретёрнули через C++ OLC-сохранение: исходные ямл-миры, полученные через
`tools/converter/convert_to_yaml.py`, выходили без бага, потому что
ruamel.yaml автоматически квотит скаляры с лидирующими индикаторами
(`preserve_quotes=True`). Поэтому наши прогоны `run_load_tests.sh` на
свежесконвертированном мире проходили чисто, а на стрибоговом
зеркале `/home/stribog/lib/world` boot падал.

Тесты: `tests/koi8r.yaml.emitter.cpp` гоняет save -> reload через
yaml-cpp для скаляров, начинающихся с `&`, `*`, `!`, `,` и для
исходного кейса `&Yfoo &Wbar&n` из тикета. Без фикса три теста падают,
с фиксом — все девять зелёные.
@bylins
Copy link
Copy Markdown
Owner

bylins commented May 17, 2026

Прогнал git rebase master локально — оба коммита (00bdea58a refactor + 3c7d087a1 fix) пропускаются как previously applied commit:

warning: skipped previously applied commit 00bdea58a
warning: skipped previously applied commit 3c7d087a1
Successfully rebased and updated refs/heads/fix/yaml-emitter-quote-indicators-3273.

После ребейза ветка идентична master (git diff master пустой). Работа этого PR уже влилась в master другим путём — через 71a0802f9 refactor(yaml): вынес Koi8rYamlEmitter в отдельный header и 9827acebc fix(yaml): эмиттер должен квотить скаляры с YAML-индикаторами в начале (#3273).

Поверх master ушли ещё 25 коммитов (#3278 / #3279 и др.), в т. ч. дополнительные фиксы литерал-блоков (|2/|-2, clip/strip), SaveX → index.yaml, новые roundtrip-тесты, конвертерные правки. Если форс-пушнуть ребейзнутую ветку, PR станет пустым.

@kvirund — предлагаю закрыть PR как obsolete, дублирующая работа в master. Если есть что-то, чего тут было, а в master не доехало (хотел бы знать что — хедер по диффу выглядит более развитой версией твоего), скажи, перенесём отдельным коммитом.

@kvirund kvirund closed this May 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

не грузится текущий yaml мир

2 participants