Diablo II, дни с двенадцатого по семнадцатый
Пост про дни с седьмого по одиннадцатый остановился 21 октября в 12:11: на карте исследования 36 строк, 35 из них после ревью, а первому сквозному запуску спецификаций ядра до планки не хватало около шестнадцати сессий. Этот пост доходит до 27 октября, 18:44. Теперь на карте 42 строки, и 38 из них после ревью. Цепочка от сида игры до предмета на полу прошла свою планку в тот же день после обеда. У фазы навыков, третьей фазы симуляции, есть спецификация после ревью. У четвёртой, анимации и тайминга кадров, есть находки и первый запуск. Пятая, ИИ монстров, одобрена, а за ней добавлена шестая.
Шесть дней, из них рабочих три. 21-го линии работали до 19:30 с небольшим; 25-го — около полутора часов вечером; 27-го — с 06:42 до отсечки. 22-го, 23-го, 24-го и 26-го не было ни одного коммита: у меня кончился недельный лимит. Почти всё, о чём пойдёт речь, случилось 27-го: из 146 строк, добавленных в журнал ревью за эти дни, 117 датированы этим днём. Между ними, в три часа ночи 25-го, кончилось летнее время, так что время 21-го указано по UTC+2, а остальное — по UTC+1.
Я и на этот раз не играл. Что я делал, по записям: 21-го, уже после отсечки прошлого поста, шесть раз просил поставить линии на паузу, потому что кончался недельный лимит. 25-го одобрил два плана и порядок работы, третий придержал и вычеркнул одну из сессий, которые оставили мне сыграть самому, — запись, оставшуюся от плана 0042. В тот же вечер линии остановил мой лимит использования. 27-го я вписал одну заметку в порядок запусков, попросил ускорить гейт коммитов, одобрил план по ИИ монстров и его решение о рамках, согласился опробовать новую схему ревью и одобрил план, который починил гейт.
Карта работы
Карта исследования к каждой отсечке
- не начато
- в плане
- находки
- черновик спецификации
- спецификация после ревью
3 спецификация после ревью 1 находки 1 в плане 23 не начато из 28
Ядро
- rng спецификация после ревью
- items/tc находки
- items/quality не начато
- items/affixes не начато
- items/uniques-sets не начато
- items/properties не начато
- monsters/pool не начато
- monsters/spawn не начато
- monsters/special не начато
- drlg/overview в плане
- drlg/preset не начато
- drlg/maze не начато
- drlg/outdoor не начато
- drlg/warps не начато
- objects не начато
Опоры
- data/excel спецификация после ревью
- data/calc не начато
- data/strings не начато
- rng/seeds спецификация после ревью
- drlg/collision не начато
- save/d2s не начато
Пути создания
- items/vendors не начато
- items/cube не начато
- items/sockets-runewords не начато
- items/quest-drops не начато
- monsters/stats не начато
- hirelings не начато
- player/creation не начато
28 спецификация после ревью 1 в плане из 29
Ядро
- rng спецификация после ревью
- items/tc спецификация после ревью ↑
- items/quality спецификация после ревью ↑
- items/affixes спецификация после ревью ↑
- items/uniques-sets спецификация после ревью ↑
- items/properties спецификация после ревью ↑
- monsters/pool спецификация после ревью ↑
- monsters/spawn спецификация после ревью ↑
- monsters/special спецификация после ревью ↑
- drlg/overview спецификация после ревью ↑
- drlg/preset спецификация после ревью ↑
- drlg/maze спецификация после ревью ↑
- drlg/outdoor спецификация после ревью ↑
- drlg/warps спецификация после ревью ↑
- objects спецификация после ревью ↑
Опоры
- data/excel спецификация после ревью
- data/calc спецификация после ревью ↑
- data/strings спецификация после ревью ↑
- rng/seeds спецификация после ревью
- drlg/collision спецификация после ревью ↑
- save/d2s спецификация после ревью ↑
Пути создания
- items/vendors спецификация после ревью ↑
- items/cube спецификация после ревью ↑
- items/sockets-runewords спецификация после ревью ↑
- items/quest-drops спецификация после ревью ↑
- monsters/stats спецификация после ревью ↑
- hirelings спецификация после ревью ↑
- player/creation спецификация после ревью ↑
Симуляция
- combat/formulas в плане
35 спецификация после ревью 1 находки из 36
Ядро
- rng спецификация после ревью
- items/tc спецификация после ревью
- items/quality спецификация после ревью
- items/affixes спецификация после ревью
- items/uniques-sets спецификация после ревью
- items/properties спецификация после ревью
- monsters/pool спецификация после ревью
- monsters/spawn спецификация после ревью
- monsters/special спецификация после ревью
- drlg/overview спецификация после ревью
- drlg/preset спецификация после ревью
- drlg/maze спецификация после ревью
- drlg/outdoor спецификация после ревью
- drlg/warps спецификация после ревью
- objects спецификация после ревью
- chain находки
Опоры
- data/excel спецификация после ревью
- data/calc спецификация после ревью
- data/strings спецификация после ревью
- rng/seeds спецификация после ревью
- drlg/collision спецификация после ревью
- save/d2s спецификация после ревью
- drlg/lifecycle спецификация после ревью
Пути создания
- items/vendors спецификация после ревью
- items/cube спецификация после ревью
- items/sockets-runewords спецификация после ревью
- items/quest-drops спецификация после ревью
- monsters/stats спецификация после ревью
- hirelings спецификация после ревью
- player/creation спецификация после ревью
Симуляция
- combat/formulas спецификация после ревью ↑
- stats/aggregate спецификация после ревью
- stats/states спецификация после ревью
Правила игры
- quests/progression спецификация после ревью
- quests/travel спецификация после ревью
- items/economy спецификация после ревью
38 спецификация после ревью 3 находки 1 в плане из 42
Ядро
- rng спецификация после ревью
- items/tc спецификация после ревью
- items/quality спецификация после ревью
- items/affixes спецификация после ревью
- items/uniques-sets спецификация после ревью
- items/properties спецификация после ревью
- monsters/pool спецификация после ревью
- monsters/spawn спецификация после ревью
- monsters/special спецификация после ревью
- drlg/overview спецификация после ревью
- drlg/preset спецификация после ревью
- drlg/maze спецификация после ревью
- drlg/outdoor спецификация после ревью
- drlg/warps спецификация после ревью
- objects спецификация после ревью
- chain спецификация после ревью ↑
Опоры
- data/excel спецификация после ревью
- data/calc спецификация после ревью
- data/strings спецификация после ревью
- rng/seeds спецификация после ревью
- drlg/collision спецификация после ревью
- save/d2s спецификация после ревью
- drlg/lifecycle спецификация после ревью
Пути создания
- items/vendors спецификация после ревью
- items/cube спецификация после ревью
- items/sockets-runewords спецификация после ревью
- items/quest-drops спецификация после ревью
- monsters/stats спецификация после ревью
- hirelings спецификация после ревью
- player/creation спецификация после ревью
Симуляция
- combat/formulas спецификация после ревью
- stats/aggregate спецификация после ревью
- stats/states спецификация после ревью
- skills/dispatch спецификация после ревью
- skills/missiles спецификация после ревью
- skills/catalogue находки
- anim/timing находки
- anim/movement находки
- monsters/ai в плане
Правила игры
- quests/progression спецификация после ревью
- quests/travel спецификация после ревью
- items/economy спецификация после ревью
Стрелка отмечает строку, которая сдвинулась с прошлого снимка. Источник: docs/specs/README.md на коммитах 8faba4f2, 33dc648, 483728e и a2d31a99. Все шесть новых строк — в полосе «Симуляция».
Показать таблицей
| Подсистема | День 1 | День 6 | День 11 | День 17 |
|---|---|---|---|---|
rng | спецификация после ревью | спецификация после ревью | спецификация после ревью | спецификация после ревью |
items/tc | находки | спецификация после ревью | спецификация после ревью | спецификация после ревью |
items/quality | не начато | спецификация после ревью | спецификация после ревью | спецификация после ревью |
items/affixes | не начато | спецификация после ревью | спецификация после ревью | спецификация после ревью |
items/uniques-sets | не начато | спецификация после ревью | спецификация после ревью | спецификация после ревью |
items/properties | не начато | спецификация после ревью | спецификация после ревью | спецификация после ревью |
monsters/pool | не начато | спецификация после ревью | спецификация после ревью | спецификация после ревью |
monsters/spawn | не начато | спецификация после ревью | спецификация после ревью | спецификация после ревью |
monsters/special | не начато | спецификация после ревью | спецификация после ревью | спецификация после ревью |
drlg/overview | в плане | спецификация после ревью | спецификация после ревью | спецификация после ревью |
drlg/preset | не начато | спецификация после ревью | спецификация после ревью | спецификация после ревью |
drlg/maze | не начато | спецификация после ревью | спецификация после ревью | спецификация после ревью |
drlg/outdoor | не начато | спецификация после ревью | спецификация после ревью | спецификация после ревью |
drlg/warps | не начато | спецификация после ревью | спецификация после ревью | спецификация после ревью |
objects | не начато | спецификация после ревью | спецификация после ревью | спецификация после ревью |
chain | — | — | находки | спецификация после ревью |
data/excel | спецификация после ревью | спецификация после ревью | спецификация после ревью | спецификация после ревью |
data/calc | не начато | спецификация после ревью | спецификация после ревью | спецификация после ревью |
data/strings | не начато | спецификация после ревью | спецификация после ревью | спецификация после ревью |
rng/seeds | спецификация после ревью | спецификация после ревью | спецификация после ревью | спецификация после ревью |
drlg/collision | не начато | спецификация после ревью | спецификация после ревью | спецификация после ревью |
save/d2s | не начато | спецификация после ревью | спецификация после ревью | спецификация после ревью |
drlg/lifecycle | — | — | спецификация после ревью | спецификация после ревью |
items/vendors | не начато | спецификация после ревью | спецификация после ревью | спецификация после ревью |
items/cube | не начато | спецификация после ревью | спецификация после ревью | спецификация после ревью |
items/sockets-runewords | не начато | спецификация после ревью | спецификация после ревью | спецификация после ревью |
items/quest-drops | не начато | спецификация после ревью | спецификация после ревью | спецификация после ревью |
monsters/stats | не начато | спецификация после ревью | спецификация после ревью | спецификация после ревью |
hirelings | не начато | спецификация после ревью | спецификация после ревью | спецификация после ревью |
player/creation | не начато | спецификация после ревью | спецификация после ревью | спецификация после ревью |
combat/formulas | — | в плане | спецификация после ревью | спецификация после ревью |
stats/aggregate | — | — | спецификация после ревью | спецификация после ревью |
stats/states | — | — | спецификация после ревью | спецификация после ревью |
skills/dispatch | — | — | — | спецификация после ревью |
skills/missiles | — | — | — | спецификация после ревью |
skills/catalogue | — | — | — | находки |
anim/timing | — | — | — | находки |
anim/movement | — | — | — | находки |
monsters/ai | — | — | — | в плане |
quests/progression | — | — | спецификация после ревью | спецификация после ревью |
quests/travel | — | — | спецификация после ревью | спецификация после ревью |
items/economy | — | — | спецификация после ревью | спецификация после ревью |
Одна строка сдвинулась, шесть новых. chain перешла из находок в «после ревью». Две строки навыков, диспетчеризация и снаряды, новые и сразу после ревью: их добавили во время фазы навыков, а спецификация по ним прошла ревью 27-го. У третьей, каталога всех нумерованных функций навыков, находки готовы, а спецификации пока нет. У фазы анимации две строки с находками: тайминг и перемещение. А monsters/ai стоит на карте как запланированная: план одобрен, первая фаза прошла ревью, находок нет.
От сида к миру, запуск 2
В прошлом посте план 0048 остановился на 1 222 заселениях комнат из 2 000 и двух расхождениях в модели объектов. Ревью объяснило их так: это монстры, созданные заново после того, как их комнату усыпили и разбудили, и залогированной отметки у них нет. Запуск 2 закрыл оба вопроса. Пересоздания идут через RoomInactiveUnitsRestore и отмечают карту коллизий теми же вызовами UNIT_CollMark, что и первое создание; логгер их пропускал, потому что они выполняются вне его окна. Когда он начал их ловить, запуск 21-го дал 37 логов и ни одной проваленной проверки:
Из 7 290 пересозданий 1 903 сверили построчно с тем, что записала сама игра: 1 729 живых, 174 мёртвых. Контроль: при запуске с --cf recreation-off модель «воспроизводит ровно те» два расхождения запуска 1, а если выключить другой механизм, снаряжение монстров, ломаются два новых случая, оба на Blood Raven. Ревью закрыло фазу в 13:11 и решило, насколько засчитывать непроверенную часть: 5 284 пересоздания, применённые к логам запуска 1, где сравнивать было не с чем, «годятся для 0 расхождений и для заселений, но не как доказательство отметок».
Спецификация, specs/chain/README.md, прошла ревью в 13:51: 89 подтверждённых утверждений ([C]: прочитано в коде и подтверждено запуском) против 7 выведенных ([I]: только прочитано в коде). Вики-страница прошла ревью в 14:19. Первой же правкой ревью вики убрало фразу, которую я сам написал в графике прошлого поста: «от одного сида игры». Теперь на странице сказано, что 37 игр восстановлены с нулём расхождений, «если взять из записи маршрут и исходы боёв и несколько счётных значений, которые всё ещё читаются из неё». Выходит, мир цепочка предсказывает по сиду, а то, что в нём делал игрок, — шаги и бои — берёт из записи. План 0048 закрылся 21-го в 14:23.
Навыки
План 0045 — это механика навыков и снарядов: диспетчеризация, сетевые пакеты, снаряды, обработчики событий. В прошлом посте у него была закрыта статическая фаза, а пять утверждений сообщества опровергнуты на бумаге. Фаза 2 началась 21-го, дважды вставала на паузу по моей просьбе и прошла 25-го: 18 сессий с восемью персонажами, 335 618 проверок, ноль расхождений. Вторая часть, 27-го, добавила 15 сессий, тоже без расхождений, и перепроверила 18 логов первой части. Сессии проходит пилот, скрипт проекта, который ведёт игру по скриншотам, а навык виден в логах как две функции: стартовая, которая выполняется, когда каст начинается, и функция действия, которая выполняется на кадре действия.
Пять опровержений и что с ними вышло в запусках:
- Уровень Raise Skeleton. Сообщество считает, что скелет поднимается на уровне заклинателя. Код даёт
min(max(L × 3/4 + уровень навыка, 1), L). У заклинателя 86-го уровня с навыком на 20 все его скелеты поднимались 84-го уровня. - Дальность снаряда. Сообщество прибавляет
LevRangeза каждый уровень после первого; код умножает его на уровень. Проверяли на Blaze, у которогоLevRangeравен 25, и первая часть этого не получила: «пилот телепортируется», так что пешего огненного следа в логе нет. Во второй части пилот ходил пешком: 271 создание Blaze на втором уровне навыка, и у каждого дальность по коду, а не по версии сообщества: там счёт идёт со второго уровня, иLevRangeвышло бы на один меньше. - Ускорение прибавляется на каждом пятом шаге пути снаряда, а не каждый кадр: 1 657 шагов с ускорением, из них 278 пятых, 60 упёрлись в предел.
- Снаряд с пробиванием, у которого не прошёл бросок на попадание, по словам сообщества, летит дальше и урона не наносит. Код заканчивает его на этом юните.
- Скорость снаряда,
((VelLev × lvl) / 8 + Vel) << 8, — единственное, чего запуски показать не смогли. Все монстры, которые стреляют нужным снарядом, попались на первом уровне, где слагаемое с уровнем равно нулю: «22 созданияspike2… все на 1-м уровне». Решили не отправлять пилота на Nightmare ради quill rats; те, что в Blood Moor, остаются проверкой на случай, если попадутся.
В одном месте запуск ещё и поправил чтение. Статическая спецификация утверждала, что Raise Skeleton не берёт ни одного числа с сида заклинателя. Первые отладочные сессии 21-го видели, как сид делает один шаг после спавна, три раза из трёх, а запуск 25-го нашёл, где это число берётся: 0x005C4430.
Что было в руках у варвара во втором запуске по навыкам
-
Fleshripper
-
Lacerator
Из инструмента проекта tools/item_art.py. Рецепт SkillsBarH даёт варвару оба оружия, по одному в каждую руку, а во всех остальных слотах снаряжения — уникальные вещи с максимальными значениями. Графика Diablo II © Blizzard Entertainment.
Fleshripper — тот самый нож, который неделей раньше сломал модель боя — 101 расхождение в обработчике убийства, до которого никто не добрался с чтением. Теперь модель обработчиков воспроизводит все 3 461 старое срабатывание, включая лог той сессии, а второй запуск по навыкам дал варвару этот нож в одну руку и Lacerator в другую. «Не лечиться» нож наложил 86 раз.
Одна находка касается клиента. Навык, в котором отказано, до сервера не доходит: «клиент каждый кадр выполняет тот же SKILLS_CanUse на своей копии игрока (0x004D9FC5) и не шлёт запроса на навык, в котором отказано», так что случаи отказа — это недобор, который со стороны сервера не закрыть. Другая — Kick. У стандартной строки Kick минимальный и максимальный урон равны нулю, так что сам удар наносит ноль физического урона: 25 раз удар дошёл до COMBAT_ApplyDamage «с результатом 9, физический 0 … там, где ветка Kick в GetPhysMin дала бы не меньше 49 920». Ветка пинков, которая считает от силы и ловкости, существует, но до неё доходят Dragon Talon, Dragon Tail и Dragon Flight у ассасина, а из монстров навык с битом пинка есть только у Shadow Warrior и Shadow Master.
Фаза закрылась 27-го. Спецификации прошли ревью в 09:30 с девятью правками; одна из них — что при лимите питомцев убирается «первый в списке», а не «самый старый». Вики-страница — в 09:58, ещё с девятью; среди них «Kick „даёт фиксированный удар“», отмеченное как завышение.
Каталог, 310 из 310
План 0046, каталог всех нумерованных функций навыков, прогоняет их по одной в эмуляторе процессора из Ghidra, как описывал прошлый пост. Последние четыре пакета прошли 21-го, 42 600 случаев, ноль расхождений, и фаза 1 закрылась на 310 записях из 310. Что показал эмулятор:
- Функция монстра do 152 «— это do 95 … инструкция в инструкцию»: 147 инструкций, ни одна не отличается.
- Do 112 накладывает одно из пяти фиксированных проклятий, а какое — решает сид заклинателя по модулю пять. У одного из пяти, Lower Resist, есть ветка сопротивления, до которой дойти нельзя.
- Взрывающиеся и ядовитые святилища роняют от пяти до девяти зелий. «От пяти до десяти» — это всего лишь текст столбца в редакторе святилищ.
- Щупальце Baal берёт число два или три раза, а сколько именно — зависит от локальной переменной, в которую его функция «ни разу не пишет до вызова … Никакая эмуляция не даст значения, которое там оставляет игра; это может только залогированное применение навыка». Утверждение остаётся выведенным и уходит в фазу на харнессе, которой нужен Baal.
Анимация и тайминг кадров
Четвёртую фазу одобрили 25-го в том порядке, который я задал 20-го: анимация и тайминг кадров раньше ИИ монстров, «потому что таймингу навыков и боя сначала нужны счётчики кадров». Первая фаза плана 0050 прочитала данные анимации: animdata.d2 — это 256 хеш-блоков из записей по 160 байт, и приоритет у файла дополнения. Ещё один файл, eanimdata.d2, вообще не упоминается ни в одной строке бинарника. События последовательностей монстров совпадают со своей записью анимации только в 81 шаге из 805.
Из чтения кода видно кое-что о том, как устроен ход игры: «большинство режимов на сервере не продвигаются кадр за кадром; SUNIT_ScheduleModeEvents сразу в начале ставит в очередь события кадров (коды 1–4) и конец режима». Скорости считает одна функция с девятью ветками, а бонус faster cast, hit recovery, block и остальных — убывающая формула B = k·s / (k + s), где k равно 120, а для бега и ходьбы 150; подтверждено на 3 413 эмулированных случаях. И крайний случай: «отрицательный коэффициент (FCR ≤ −55, FHR или FBR ≤ −36) переполняется и даёт максимальную скорость».
О брейкпоинтах сообществу есть что сказать, так что и сравнение вышло большим: 61 утверждение со страниц Maxroll и Basin, из старой темы про бег и ходьбу для 1.09, из уточнения про перемещение для 1.13 и из таблиц PureDiablo. Их записали вслепую, до того как линия прочитала код.
Сами таблицы брейкпоинтов оказались верны: «каждая точка смены совпадает (78/78)». Число кадров в 76 из них на единицу меньше, чем у кода, и ревью признало это разницей в способе счёта, а не ошибкой. Вот что опровергнуто, среди прочего. Из утверждений о самой 1.14d: коэффициент атаки ограничен значениями от 15 до 175, а у hit recovery и блока потолка нет вообще; после отбрасывания игрок оказывается в нейтральной позе без hit recovery; а у блока есть перезарядка, frame − last block > ⌊stat102 / 8⌋ + 15, хотя сообщество говорит, что её нет. Из источников по Resurrected: база Double Swing — 70, а не 120, как у Maxroll, и у друида, некроманта, Werewolf и Werebear кадры каста отличаются у всех. Из темы по 1.09: на Charge персонаж движется в 2,5 раза быстрее базовой скорости, а не в 1,5, а коэффициент бега «65» «сходится, только если читать его как hex». 0x65 — это 101.
Первый запуск фазы прошёл 27-го: 55 сессий, ноль расхождений, 298 373 вызова расчёта скорости, 99 241 шаг перемещения. На Fallen видно, что особые режимы классического монстра берут скорость движения, 1 266 вызовов. Blood Raven на Hell отбросила игрока 13 раз, и в 12 из них он оказался в нейтральной позе в том же кадре. Следующий каст Frozen Orb приходился на кадр действия плюс задержку, 72 из 73. Недобор — get-hit, реакция на удар: «каждый хрупкий персонаж умирал после 2–9: для get-hit нужен удар на шестнадцатую часть жизни».
ИИ монстров и шестая фаза
План 0052 написали 25-го, придержали, переписали 27-го, и в 17:34 я его одобрил. Он покрывает «331 пробел по монстрам», оставшийся после запусков по навыкам, и строится вокруг таблицы ИИ по адресу 0x0073CA18, 148 строк по 16 байт. План задал мне восемь вопросов. На семь я ответил так, как рекомендовалось: поиск пути станет отдельным планом, наёмники и призванные входят в рамки, слот харнесса один и так далее. На восьмой я ответил против рекомендации, и в плане это так и записано: «Покрытие каталога: вся таблица ИИ (не как в рекомендации). Каждая строка таблицы ИИ получает запись в каталоге, независимо от того, ссылается ли на неё стандартная строка MonStats, PetType или Hireling».
Фаза 0 прошла меньше чем за час. Из 148 строк на 146 ссылается какой-нибудь стандартный монстр; до 63 игра доходит через пулы уровней, до 44 — через суперуникальных, до 16 — через призыв, до одной — через наёмников. Поле aidist задано только у Mephisto и его Uber-копии. А ревью нашло то, чего фаза не искала: вторую таблицу ИИ по адресу 0x0073D358, восемнадцать строк по четыре слова, и в десяти из них функции, которых нет ни в одной строке первой таблицы.
Тем же одобрением я принял и ADR-0022. Фазы проекта заканчиваются на ИИ монстров, и ADR добавляет после него ещё одну, кадр сервера: «игровой тик (GameTick и каждый проход, который он делает), порядок обновления юнитов … очередь событий … таймеры … и порядок активации комнат внутри кадра». Доказательство для неё тоже новое, окно кадров: «заявленная серия последовательных кадров в небольшой сцене … в которой залогировано каждое относящееся к фазе событие каждого юнита и проверен каждый входной сид, без единой повторной синхронизации». До сих пор любая проверка сверяла выход функции с её залогированными входами; окно кадров проверяет, что между ними ничего не пропущено. ADR прямо называет свой риск: «Под wine порядок может оказаться отчасти недетерминированным». В моей утренней заметке было сказано, что пятая фаза — последняя в старых рамках, а для всего, что дальше, нужен ADR о рамках. ADR-0022 и есть этот ADR.
Ревизия
План 0038 разложил по пакетам 1 526 выведенных утверждений из спецификаций после ревью. За эти дни прошли одиннадцать пакетов на харнессе, десять из них 27-го.
Кое-что из найденного пакетами видно и игроку:
- В одиночной игре сохранение происходит при смерти. H1b нашёл этот вызов. Таймер автосохранения за весь запуск не сохранил ничего.
- Если золота больше лимита, при загрузке оно обнуляется целиком. Персонаж с 900 000 золотых при себе, больше потолка в 800 000, загружается без золота; стэш с 3 000 000, больше 2 500 000, загружается пустым.
- Персонаж класса 7, следующего за последним классом в файле сохранения, при загрузке останавливает игру с внутренней ошибкой.
- Крафтовый предмет может уронить игру. Когда крафт в Кубе выбирает аффикс из пустого списка при низком уровне предмета, игра падает с нарушением доступа. H6a сохранил это как доказательство.
- Коровий портал в классической игре проверяет слово квеста 26, а в дополнении — слово 40.
- Сундуки Khalim сначала роняют золото, а потом саму часть: пять кучек перед глазом, семь перед мозгом, а в сундуке с сердцем — восемь. Уровень с мозгом сервер строит дважды, и до правки 27-го пилот перечислял объекты только одной копии.
- Malah предлагает зелье только в одном состоянии квеста, 4. Бесплатный наёмник от Kashya выдаётся без списания денег. Наёмник получает двойной опыт, и только пока его уровень ниже, чем у хозяина.
Три части, ради которых пакет по квестовым предметам открывал сундуки
-
Khalim's Eye
-
Khalim's Brain
-
Khalim's Heart
Из tools/item_art.py. Пакет-продолжение открыл все три сундука, и порядок, золото раньше части, подтверждён для каждого. Графика Diablo II © Blizzard Entertainment.
H4a, пакет по монстрам, за одну сессию открыл четыре печати из пяти в Chaos Sanctuary и залогировал Infector of Souls и Grand Vizier of Chaos. До пятой печати так и не дошли, поэтому нет ни Lord De Seis, ни Diablo; ревью решило эти строки «сейчас владельцу не передавать», раз похожую сессию я вычеркнул 25-го.
Два ревью проверили собственные итоги пакетов и пишут об этом одной строкой: «„Недобор 0“ — честно» про квестовых NPC и «„Поднято меньше половины“ — честно (9 из 25)» про сокеты. А один пакет нарушил правило, которого у проекта ещё не было. Зонды H3b нарочно роняли игру и писали файлы аварий в общую папку игры, и «не сохранились ни логи прогонов зондов, ни удалённые файлы аварий». Теперь правило такое: зонды, которые могут уронить игру, работают в своей папке игры, а папки аварий сохраняются.
После пакетов спецификации перетегируют и пересчитывают:
Опись на отсечке: 567 утверждений в статических пакетах, 509 в пакетах на харнессе, 431 заблокировано. Заблокированных стало больше, было 414, потому что чем внимательнее смотрит пакет, тем чаще выясняется, что у части утверждений нечего наблюдать: это мёртвый код или условие, которое на стандартных данных всегда выполняется. Таких теперь 302. В плане осталось два пакета, два перетегирования, а в конце — список того, что могу сыграть только я.
План 0047, недоборы симуляции, закрылся 25-го. Одна строка из него — про харнесс: ожидания в проверках харнесса на истечение эффектов «рассчитаны на 25 кадров в секунду, а под логгером характеристик игра идёт примерно на 17». И одна — про игру: суккубы в Crystalline Passage накладывают Defense Curse на персонажа под Fade, а это 84 процента сопротивления проклятиям, и из 275 кадров, на которые было рассчитано проклятие, оно продержалось 44.
Гейт, который перестал останавливать коммиты
В проекте есть гейт, tools/gate.py, который на каждом коммите сверяет документы друг с другом: счётчики тегов в спецификациях, перекрёстные ссылки, символы, которых никто не упоминает. Хук коммита запускает его с таймаутом в 20 секунд, и хук намеренно мягкий: «Сломанный гейт никогда не блокирует работу … таймаут … проходит с предупреждением в одну строку». Я попросил ускорить гейт, и план 0053 выяснил, во что на деле обходилась эта мягкость: «быстрый режим занимал от 153 до 183 с, из них 151 с — цикл W3 (около 50 000 вызовов re.search по склеенному корпусу), так что при таймауте хука коммита в 20 с проверка просто пропускалась». W3 ищет символ из re/symbols.csv, который не назван ни в одной спецификации и ни в одной находке. При 3 666 символах проверка гоняла каждый как регулярное выражение по всему тексту. Каждый коммит проходил с предупреждением в одну строку, и с каких пор, по записям не понять. Когда регулярные выражения заменили поиском по множеству слов, гейт по staged-изменениям стал занимать 5,6 с, W3 выдаёт те же ноль предупреждений, а хук снова может остановить коммит. План заодно добавил правила для того, что ревью раз за разом правили вручную: строка таблицы, в которой ячеек больше, чем в заголовке; статус на карте, который не совпадает с шапкой спецификации; индекс планов, который не совпадает с самим планом.
Вместе с планом 0053 пришёл ADR-0023, принятый в ту же минуту, — о том, во что обходятся ревью. Его контекст умещается в одну фразу: «Недельный лимит токенов владельца кончается примерно за день, когда работают несколько линий». Снимок журнала в нём: 362 прогона, 83,6 миллиона токенов, из них на ревью архитектора приходится 172 прогона и 30,9 процента токенов; 708 правок, 19 процентов из них механические, такие, которые мог бы поймать гейт. Поэтому теперь у ревью два уровня. Уровень A — полное ревью, для линий со спецификациями, изменений ADR и аудитов. Уровень B «идёт на Sonnet и читает только git diff main...<lane>», и «каждая пятая линия уровня B получает ещё и ревью уровня A». ADR называет, чем за это платят: «Между линиями [C] у находки слабее». Я принял его в 18:19 как эксперимент, итог которого подведёт четвёртая фаза плана после десяти линий. Ещё появился инструмент tools/metrics.py: он сводит журнал «не загружая его в контекст агента», а журнал к этому времени весит около 100 КБ.
Как это было организовано
Линии шли как раньше: линии на харнессе на слотах 1 и 2, слот 0 мой и не занят, рядом статические линии с Ghidra, у каждой линии своя копия репозитория, и каждое закрытие проходит ревью. В журнале за эти дни 78 строк линий и 68 ревью. Линии потратили 22,8 миллиона токенов, ревью — 10,5 миллиона, это около 52 и 13 часов реального времени. 27-го три долгие линии на харнессе — пакет по сохранениям, квестовые предметы и запуск по анимации — каждую примерно через четыре часа сменили новой: линия останавливается, пишет заметку для продолжения, и новая линия продолжает с неё. В записях за эти дни нет ни сна машины, ни убийства из-за нехватки памяти. И каждый раз работу останавливал мой недельный лимит: паузы 21-го, остановка 25-го и пустые дни между ними.
Числа
Где проект сейчас, в сравнении с первым, шестым и одиннадцатым днём
Спецификации после ревью (28 строк до дня 4, 29 в день 6, 35 в день 8, 36 в день 11, 42 в день 17)
[C] подтверждённые утверждения в находках
[I] выведенные утверждения в находках
Именованные адреса в Game.exe
Инструменты на Python
Закрытые планы
У каждого показателя своя шкала. Источники: карта исследования, docs/findings, re/symbols.csv, tools/ и docs/plans/done на каждой отсечке.
Подтверждённые и выведенные утверждения в находках
[C] — прочитано в коде и подтверждено чем-то независимым, обычно запуском. [I] — только прочитано в коде. Ночи и дни, когда ничего не менялось, вырезаны.
- [C] подтверждено
- [I] выведено
У 25-го одна точка — вечерний пакет по монстрам. 27-го выведенные растут быстрее: то, что прочитали в коде анимации и ИИ, идёт как [I], а перетегирования того дня сдвигают утверждения в спецификациях, а не в находках.
Показать числа
| Время | [C] | [I] |
|---|---|---|
| 11 окт. 10:31 | 0 | 0 |
| 11 окт. 13:35 | 7 | 15 |
| 11 окт. 15:05 | 21 | 58 |
| 11 окт. 16:00 | 28 | 59 |
| 11 окт. 16:25 | 42 | 62 |
| 11 окт. 17:57 | 45 | 104 |
| 11 окт. 19:06 | 48 | 148 |
| 11 окт. 20:11 | 48 | 175 |
| 11 окт. 22:12 | 57 | 176 |
| 11 окт. 22:23 | 57 | 176 |
| 12 окт. 08:04 | 80 | 173 |
| 12 окт. 08:41 | 103 | 183 |
| 12 окт. 09:02 | 104 | 184 |
| 12 окт. 09:31 | 108 | 217 |
| 12 окт. 09:58 | 109 | 228 |
| 12 окт. 10:16 | 117 | 278 |
| 12 окт. 10:42 | 135 | 295 |
| 12 окт. 11:51 | 139 | 295 |
| 12 окт. 12:21 | 159 | 291 |
| 12 окт. 13:41 | 161 | 292 |
| 12 окт. 14:10 | 168 | 295 |
| 12 окт. 15:19 | 168 | 320 |
| 12 окт. 15:46 | 184 | 354 |
| 12 окт. 15:58 | 184 | 354 |
| 13 окт. 08:05 | 196 | 348 |
| 13 окт. 08:26 | 204 | 374 |
| 13 окт. 08:33 | 205 | 422 |
| 13 окт. 10:11 | 206 | 448 |
| 13 окт. 13:43 | 223 | 449 |
| 13 окт. 15:59 | 225 | 452 |
| 13 окт. 18:56 | 229 | 453 |
| 13 окт. 19:21 | 236 | 452 |
| 13 окт. 19:28 | 240 | 455 |
| 13 окт. 21:15 | 279 | 429 |
| 13 окт. 22:02 | 279 | 442 |
| 14 окт. 08:11 | 279 | 442 |
| 14 окт. 08:49 | 282 | 443 |
| 14 окт. 09:21 | 282 | 493 |
| 14 окт. 09:36 | 282 | 495 |
| 14 окт. 09:49 | 290 | 536 |
| 14 окт. 10:00 | 291 | 537 |
| 14 окт. 10:09 | 293 | 586 |
| 14 окт. 10:20 | 293 | 587 |
| 14 окт. 11:05 | 332 | 567 |
| 14 окт. 13:10 | 356 | 562 |
| 14 окт. 17:35 | 376 | 560 |
| 14 окт. 19:22 | 376 | 611 |
| 14 окт. 19:56 | 395 | 651 |
| 14 окт. 20:36 | 402 | 691 |
| 14 окт. 20:50 | 446 | 721 |
| 14 окт. 20:59 | 451 | 757 |
| 14 окт. 21:16 | 461 | 796 |
| 14 окт. 22:37 | 506 | 772 |
| 14 окт. 22:50 | 545 | 743 |
| 15 окт. 02:10 | 574 | 733 |
| 15 окт. 02:44 | 587 | 732 |
| 15 окт. 02:54 | 612 | 737 |
| 15 окт. 03:05 | 619 | 727 |
| 15 окт. 03:29 | 629 | 739 |
| 15 окт. 03:57 | 671 | 738 |
| 15 окт. 04:14 | 711 | 716 |
| 15 окт. 07:39 | 718 | 733 |
| 15 окт. 08:47 | 729 | 737 |
| 15 окт. 08:48 | 737 | 779 |
| 15 окт. 08:54 | 746 | 782 |
| 15 окт. 09:05 | 771 | 791 |
| 15 окт. 09:17 | 773 | 848 |
| 15 окт. 19:08 | 774 | 890 |
| 15 окт. 19:10 | 786 | 941 |
| 15 окт. 19:13 | 797 | 940 |
| 15 окт. 19:25 | 801 | 982 |
| 15 окт. 19:33 | 829 | 1015 |
| 15 окт. 19:38 | 829 | 1057 |
| 15 окт. 19:48 | 841 | 1064 |
| 15 окт. 19:56 | 845 | 1066 |
| 15 окт. 20:13 | 845 | 1072 |
| 15 окт. 20:24 | 848 | 1095 |
| 15 окт. 23:42 | 875 | 1092 |
| 15 окт. 23:54 | 886 | 1095 |
| 16 окт. 00:15 | 886 | 1094 |
| 16 окт. 00:21 | 889 | 1096 |
| 16 окт. 01:43 | 905 | 1093 |
| 16 окт. 02:23 | 918 | 1098 |
| 16 окт. 02:28 | 923 | 1103 |
| 16 окт. 03:49 | 956 | 1090 |
| 16 окт. 06:40 | 987 | 1065 |
| 16 окт. 14:29 | 1003 | 1072 |
| 16 окт. 15:31 | 1023 | 1075 |
| 16 окт. 16:08 | 1023 | 1152 |
| 16 окт. 17:21 | 1080 | 1181 |
| 16 окт. 17:36 | 1140 | 1189 |
| 16 окт. 17:53 | 1150 | 1189 |
| 16 окт. 18:24 | 1174 | 1194 |
| 16 окт. 18:34 | 1228 | 1208 |
| 16 окт. 18:35 | 1267 | 1217 |
| 16 окт. 19:26 | 1281 | 1238 |
| 16 окт. 19:29 | 1321 | 1250 |
| 16 окт. 20:49 | 1391 | 1257 |
| 16 окт. 21:01 | 1412 | 1263 |
| 16 окт. 21:29 | 1486 | 1263 |
| 16 окт. 22:07 | 1505 | 1269 |
| 16 окт. 22:19 | 1515 | 1306 |
| 16 окт. 22:26 | 1566 | 1314 |
| 16 окт. 22:41 | 1577 | 1319 |
| 17 окт. 07:51 | 1577 | 1367 |
| 17 окт. 07:56 | 1585 | 1375 |
| 17 окт. 08:02 | 1597 | 1380 |
| 17 окт. 08:35 | 1612 | 1432 |
| 17 окт. 08:47 | 1650 | 1434 |
| 17 окт. 09:04 | 1647 | 1430 |
| 17 окт. 09:09 | 1647 | 1512 |
| 17 окт. 09:45 | 1657 | 1536 |
| 17 окт. 09:55 | 1669 | 1539 |
| 17 окт. 10:44 | 1670 | 1729 |
| 17 окт. 10:50 | 1670 | 1796 |
| 17 окт. 10:58 | 1670 | 1808 |
| 17 окт. 11:07 | 1681 | 1816 |
| 17 окт. 11:15 | 1683 | 1826 |
| 17 окт. 16:44 | 1707 | 1827 |
| 17 окт. 17:18 | 1707 | 1838 |
| 17 окт. 17:33 | 1707 | 1839 |
| 17 окт. 18:26 | 1708 | 1849 |
| 17 окт. 19:16 | 1746 | 1858 |
| 17 окт. 19:27 | 1747 | 1870 |
| 17 окт. 20:06 | 1747 | 1871 |
| 17 окт. 20:56 | 1770 | 1884 |
| 18 окт. 20:09 | 1773 | 1896 |
| 19 окт. 08:00 | 1788 | 1907 |
| 19 окт. 08:10 | 1804 | 1923 |
| 19 окт. 08:44 | 1805 | 1924 |
| 19 окт. 09:26 | 1814 | 1929 |
| 20 окт. 08:30 | 1849 | 1953 |
| 20 окт. 09:11 | 1849 | 1955 |
| 20 окт. 09:54 | 1853 | 2032 |
| 20 окт. 10:17 | 1856 | 2059 |
| 20 окт. 10:29 | 1914 | 2070 |
| 20 окт. 10:51 | 1917 | 2110 |
| 20 окт. 10:58 | 1947 | 2125 |
| 20 окт. 11:19 | 1973 | 2133 |
| 20 окт. 11:51 | 1973 | 2188 |
| 20 окт. 12:29 | 1973 | 2189 |
| 20 окт. 13:11 | 2022 | 2217 |
| 20 окт. 13:20 | 2036 | 2266 |
| 20 окт. 14:53 | 2037 | 2268 |
| 20 окт. 14:53 | 2076 | 2308 |
| 20 окт. 14:56 | 2099 | 2293 |
| 20 окт. 14:57 | 2108 | 2300 |
| 20 окт. 15:43 | 2110 | 2302 |
| 20 окт. 15:47 | 2154 | 2360 |
| 20 окт. 18:55 | 2178 | 2374 |
| 20 окт. 18:55 | 2230 | 2414 |
| 20 окт. 19:19 | 2230 | 2416 |
| 20 окт. 19:54 | 2272 | 2450 |
| 20 окт. 21:31 | 2312 | 2484 |
| 20 окт. 22:21 | 2347 | 2508 |
| 20 окт. 23:11 | 2381 | 2538 |
| 21 окт. 07:17 | 2420 | 2570 |
| 21 окт. 07:37 | 2431 | 2581 |
| 21 окт. 07:46 | 2445 | 2586 |
| 21 окт. 08:21 | 2479 | 2619 |
| 21 окт. 11:24 | 2489 | 2628 |
| 21 окт. 11:53 | 2539 | 2667 |
| 21 окт. 11:57 | 2540 | 2667 |
| 21 окт. 13:01 | 2579 | 2697 |
| 21 окт. 13:13 | 2584 | 2699 |
| 21 окт. 13:57 | 2626 | 2739 |
| 21 окт. 14:45 | 2663 | 2771 |
| 21 окт. 15:18 | 2667 | 2779 |
| 21 окт. 19:30 | 2700 | 2810 |
| 25 окт. 21:38 | 2718 | 2820 |
| 27 окт. 07:05 | 2737 | 2838 |
| 27 окт. 07:46 | 2750 | 2844 |
| 27 окт. 07:50 | 2760 | 2869 |
| 27 окт. 08:11 | 2760 | 2925 |
| 27 окт. 08:57 | 2779 | 2942 |
| 27 окт. 09:14 | 2810 | 2959 |
| 27 окт. 09:16 | 2814 | 3007 |
| 27 окт. 09:41 | 2817 | 3014 |
| 27 окт. 10:02 | 2836 | 3025 |
| 27 окт. 10:04 | 2836 | 3078 |
| 27 окт. 10:34 | 2840 | 3086 |
| 27 окт. 10:45 | 2840 | 3087 |
| 27 окт. 10:54 | 2874 | 3096 |
| 27 окт. 11:22 | 2877 | 3102 |
| 27 окт. 11:36 | 2891 | 3117 |
| 27 окт. 11:51 | 2891 | 3119 |
| 27 окт. 12:06 | 2896 | 3122 |
| 27 окт. 13:03 | 2897 | 3159 |
| 27 окт. 13:18 | 2914 | 3168 |
| 27 окт. 13:45 | 2920 | 3173 |
| 27 окт. 14:42 | 2934 | 3185 |
| 27 окт. 15:21 | 2956 | 3193 |
| 27 окт. 16:24 | 2984 | 3204 |
| 27 окт. 17:28 | 3005 | 3211 |
| 27 окт. 17:35 | 3032 | 3221 |
В одиннадцатый день в находках было 2 540 подтверждённых утверждений против 2 667 выведенных; теперь 3 032 против 3 221, и доля подтверждённых теперь 48,5 процента, а была 48,8. Спецификации, то есть то, что будет читать человек, который переписывает игру, сдвинулись сильнее: от 3 791 подтверждённого и 2 322 выведенных к 4 857 и 2 451, потому что это была неделя перетегирований, а перетегирование поднимает утверждения спецификации, не трогая находок. Именованных адресов в Game.exe стало 3 666 вместо 3 373, инструментов 215 вместо 170, рецептов сохранений 435 вместо 233, закрытых планов 48 вместо 44.
Что открыто
- Анимация, вторая часть запуска (get-hit в зависимости от hit recovery, блоки, метания, ступени скоростей) и её спецификация; они идут параллельно.
- ИИ монстров, план 0052, начиная с фазы каталога, и вторая таблица отдана туда же. Потом кадр сервера, который получит строку на карте, когда для него напишут план.
- Фаза каталога на харнессе, после запуска по анимации, и в ней щупальце Baal.
- Хвост ревизии: два пакета, два перетегирования и список для меня.
- То, что сыграть могу только я, всё необязательное: Uber Mephisto и Baal, Countess и Cow King в нужных состояниях квестов, Hellforge на Hell, где пилот ни в одной из четырёх попыток не добрался до комнаты, и пара сессий с Diablo, где поиск у пилота закончился по таймауту.
- Уровни ревью: следующие десять линий без спецификаций идут через проверку уровня B, а пятая и десятая проходят аудит на уровне A.
У воксельного движка в те же дни было своё окно, и в нём появилось освещение.