Симптом
В квесте «Сказка для охотника» (зона 65) гнездо с птенцами (obj 6505) на ресете зоны кладётся не всегда в ту комнату. Квест ожидает гнездо в дереве в комнате 6510 («На краю оврага»), но фактически оно попадает в одно из четырёх одинаковых сухих деревьев (obj 6503): комнаты 6510 / 6530 / 6532 / 6534.
Когда гнездо легло не в 6510, игрок приходит на правильную клетку, видит сухое дерево, но осмотреть дерево → «Внутри ничего нет», взять гнездо дерево → «не видите гнезд». Квест «не работает». Раз на раз — то работает, то нет.
Воспроизведение
zreset 65 несколько раз подряд, и после каждого где гнездо (иммом):
| zreset |
комната, куда легло гнездо 6505 |
| 1 |
6532 «В гуще леса» ← НЕ та |
| 2 |
6510 «На краю оврага» ← верно |
| 3 |
6510 «На краю оврага» ← верно |
Выдержка из лога игрока (Ивета), попавшего на «плохой» ресет, — она на правильной клетке 6510:
На краю оврага [6510]
...
Высокое сухое дерево, кажется, готово вот-вот упасть.
рубить дерево
Вы начали усердно рубить сухое дерево коротким топориком.
Откуда ни возьмись, выскочила птичка-невеличка и стала вокруг Вас летать.
вз гнезд дер
Вы не видите 'гнезд' в сухом дереве.
ос дер
Давно погибшее, но еще не упавшее дерево ... крупное дупло.
Состояние: идеально. сухое дерево(на земле)
Внутри ничего нет.
Доказательство из логов движка
[Extract obj] при ресете печатает комнату, где лежал старый экземпляр гнезда. Видно, что гнездо реально жило то в 6510, то в 6532:
2026-05-30 19:31:11 [Extract obj] Start for: хрупкое гнездо с птенцами vnum == 6505 room = 6532
2026-05-30 19:44:48 [Extract obj] Start for: хрупкое гнездо с птенцами vnum == 6505 room = 6510
2026-05-31 00:51:18 [Extract obj] Start for: хрупкое гнездо с птенцами vnum == 6505 room = 6532
То есть игрок смотрел в правильной комнате — гнездо просто оказалось в другом дереве.
Причина (код)
В zon/65.zon:
P 0 6505 0 6503 -1 (хрупкое гнездо с птенцами)
^ arg2 = 0 → искать контейнер по всему миру
При arg2 == 0 движок (ResetZoneEssential, case 'P', src/engine/db/db.cpp) ищет контейнер глобально: SearchObjByRnum(arg3) → WorldObjects::find_first_by_rnum (src/engine/core/handler.cpp, src/engine/db/world_objects.cpp). А find_first_by_rnum возвращает первый элемент std::unordered_set (m_rnum_to_object_ptr). Порядок в этом сете определяется хешами указателей (адресами в куче), то есть какое из четырёх деревьев 6503 окажется «первым» — не определено.
Почему проявилось после перехода на YAML
Данные зоны 65 (zon/obj/trg/wld) не менялись (сверял world.20260516 ↔ world.20260530 — идентичны). Баг латентный, был всегда. На legacy-загрузке раскладка кучи стабильно ставила дерево 6510 «первым» (квест казался рабочим). YAML-бутстрап (другой парс-путь, тред-пул, другой порядок аллокаций прототипов) сдвинул порядок в unordered_set — и «плохие» исходы стали частыми. Отсюда ощущение «вдруг сломалось после перехода на Ямл».
Как починить — данные (быстро, от формата не зависит)
Привязать раскладку гнезда к комнате 6510 (тогда движок идёт по ветке GetObjByRnumInContent(arg3, world[6510]->contents)):
- P 0 6505 0 6503 -1
+ P 0 6505 6510 6503 -1
Дерево 6510 грузится строкой выше (O 0 6503 -1 6510), контейнер на месте.
YAML (zones/65/zone.yaml):
- - PUT 0 6505 0 6503 -1
+ - PUT 0 6505 6510 6503 -1
OLC (zedit), на работающем сервере:
zedit 65
F — показать все команды
- найти строку
поместить 6505 [...] в 6503 [...] (в комнате 0 [везде]), запомнить её номер
E, ввести этот номер
- промпты: предмет →
6505, Комната → 6510, контейнер → 6503, вероятность → -1
X, сохранить зону
Как починить — движок (рекомендуется)
Сделать выбор контейнера в P при arg2 == 0 детерминированным: искать по списку мира / в порядке создания, а не брать «первый из unordered_set». Иначе любой квест с несколькими одинаковыми контейнерами-целями так же будет лотереей.
Симптом
В квесте «Сказка для охотника» (зона 65) гнездо с птенцами (obj 6505) на ресете зоны кладётся не всегда в ту комнату. Квест ожидает гнездо в дереве в комнате 6510 («На краю оврага»), но фактически оно попадает в одно из четырёх одинаковых сухих деревьев (obj 6503): комнаты 6510 / 6530 / 6532 / 6534.
Когда гнездо легло не в 6510, игрок приходит на правильную клетку, видит сухое дерево, но
осмотреть дерево→ «Внутри ничего нет»,взять гнездо дерево→ «не видите гнезд». Квест «не работает». Раз на раз — то работает, то нет.Воспроизведение
zreset 65несколько раз подряд, и после каждогогде гнездо(иммом):Выдержка из лога игрока (Ивета), попавшего на «плохой» ресет, — она на правильной клетке 6510:
Доказательство из логов движка
[Extract obj]при ресете печатает комнату, где лежал старый экземпляр гнезда. Видно, что гнездо реально жило то в 6510, то в 6532:То есть игрок смотрел в правильной комнате — гнездо просто оказалось в другом дереве.
Причина (код)
В
zon/65.zon:При
arg2 == 0движок (ResetZoneEssential,case 'P',src/engine/db/db.cpp) ищет контейнер глобально:SearchObjByRnum(arg3)→WorldObjects::find_first_by_rnum(src/engine/core/handler.cpp,src/engine/db/world_objects.cpp). Аfind_first_by_rnumвозвращает первый элементstd::unordered_set(m_rnum_to_object_ptr). Порядок в этом сете определяется хешами указателей (адресами в куче), то есть какое из четырёх деревьев 6503 окажется «первым» — не определено.Почему проявилось после перехода на YAML
Данные зоны 65 (
zon/obj/trg/wld) не менялись (сверялworld.20260516↔world.20260530— идентичны). Баг латентный, был всегда. На legacy-загрузке раскладка кучи стабильно ставила дерево 6510 «первым» (квест казался рабочим). YAML-бутстрап (другой парс-путь, тред-пул, другой порядок аллокаций прототипов) сдвинул порядок вunordered_set— и «плохие» исходы стали частыми. Отсюда ощущение «вдруг сломалось после перехода на Ямл».Как починить — данные (быстро, от формата не зависит)
Привязать раскладку гнезда к комнате 6510 (тогда движок идёт по ветке
GetObjByRnumInContent(arg3, world[6510]->contents)):Дерево 6510 грузится строкой выше (
O 0 6503 -1 6510), контейнер на месте.YAML (
zones/65/zone.yaml):OLC (zedit), на работающем сервере:
zedit 65F— показать все командыпоместить 6505 [...] в 6503 [...] (в комнате 0 [везде]), запомнить её номерE, ввести этот номер6505, Комната →6510, контейнер →6503, вероятность →-1X, сохранить зонуКак починить — движок (рекомендуется)
Сделать выбор контейнера в
Pприarg2 == 0детерминированным: искать по списку мира / в порядке создания, а не брать «первый изunordered_set». Иначе любой квест с несколькими одинаковыми контейнерами-целями так же будет лотереей.