Пятничные факты #242 – Наступательное программирование

Поделиться

Здравствуйте,
этот пост будет более техническим, чем обычно, но все же может быть интересно узнать предысторию процесса для некоторых людей.

Почему возникает так много проблем с загрузкой сохранений  в экспериментальных версиях?

0.16.36 является стабильным, но поскольку нам необходимо исправить некоторые дополнительные ошибки, мы начали выпуск экспериментальных выпусков 0.16.37 до 0.16.42 и так далее. С точки зрения игрока, появилось много новых ошибок, невыгружаемых сейвов, странных проблем с поездами или даже печально известной версии 0.16.40, которая отключила все сигналы, вызывающие транспортный колапс, от которого даже ребенок закричал.

Большинство из этих проблем (помимо обучения) были фактически вызваны целенаправленно. Это может показаться странным, но это должно иметь смысл для вас в ближайшее время.

Наступательное программирование

Насколько я могу судить, наступательное программирование – лучший способ сохранить сложную кодовую базу как Factorio относительно без ошибок в долгосрочной перспективе. Общая идея заключается в том, что, когда мы имеем какое-то правило того, что в коде всегда верно независимо от того, что (это называется инвариантом), мы не должны просто игнорировать его, если правило нарушено.

Инвариантные примеры в факторио, которые были нарушены

Один из инвариантов, который у нас был, заключался в том, что никогда не может быть стены поверх другой стены. Это обычно невозможно сделать, поскольку игрок не может просто построить стену над другой стеной, но со сценарием вы можете размещать объекты даже таким образом, чтобы они сталкивались. Но в случае с стенами вы не можете даже построить две стены друг над другом со сценарием. Причиной этого является то, что стены соединяются друг с другом, и поскольку в одном месте будет две стены, в мультиплеере может случиться, что соседняя стена может соединяться с разными стенами для разных игроков, которые в конечном итоге могут (и это также сделал) вызвать desyncs. Подобные инварианты установлены для конвейеров, труб и рельс.

Вторая часть проблемы возникла, когда планировщик и чертежи деконструкции вступили в игру, поскольку мы постепенно изменили игру таким образом, чтобы вы могли отмечать любую область фабрики для деконструкции и планировать чертеж над ней еще до того как область была очищена. Так внезапно вы можете иметь конвейер, обозначенный для деконструкции, с другим конвейером(в форме призрака) поверх него следующим образом:

Третья часть проблемы заключалась в том, что мы решили, что призрачные конвейеры и стены должны соединяться друг с другом, чтобы они выглядели красиво, как описано в fff-211

Чтобы эти три вещи сосуществовали, нам пришлось внести несколько изменений. Первое, что нужно было изменить, – это то, что вы, возможно, заметили: конвейеры и стены отсоединяются при маркировке для деконструкции.

Во-вторых, чтобы стены (или конвейеры) находились друг над другом, пока один обозначен для деконструкции, а другой – призрак. Поскольку вещи, отмеченные для деконструкции, не являются кандидатами на присоединение, кандидат на соединение все еще четко определен.Вы можете себе представить, что уверенность в том, что первый инвариант по-прежнему верен, может быть не таким тривиальным, например:

  • Убедитесь, что при завершении деконструкции удаляется призрак поверх объекта, помеченного для деконструкции.
  • Убедитесь, что использование телепорта не выполняется, если результат будет противоречить инварианту.
  • Удостоверьтесь, нет других способов, которыми это может случиться, мы просто не знаем.

Последний момент – самая большая проблема, потому что, если есть какой-то другой способ, который может быть нарушен инвариант, я хочу знать об этом, а не исследовать очень сложные отчеты desync. Но как я могу проверить, что это никогда не происходит, не влияя на производительность в обычных играх? Для такого рода вещей у нас есть метод, который мы называем «проверка согласованности». Он проходит через всю карту и проверяет в нем различную целостность. Проверки занимают довольно много времени, поэтому вызов его при каждом сохранении / загрузке слишком сильно повлияет на игру, поэтому мы решили вызвать это только при переходе на новую версию, которая включает в себя также переход любой версии любого мода. Проверка также может выполняться вручную с помощью команды консоли:

/c game.consistency_check()
Вопрос в том, что делать, когда проверка не удалась? Чтобы убедиться, что на самом деле это будет сообщено, мы решили, что при проверке согласованности игра мгновенно останавливается (сбой) и записывает трассировку причины и стека в журнал. Это заставляет пользователя (по крайней мере, некоторые из них) дать нам отчет об ошибке, поэтому мы можем попытаться выяснить, что происходит. После этого мы можем просто повторно активировать миграцию, которая удаляет конфликтующие объекты в новой версии, чтобы снова сохранить загрузку.

Ошибки поездов

Все ошибки, связанные с поездами, также исходили из одной и той же проблемы. Я понял, что рельсовые сигналы, обозначенные для деконструкции, не отсоединились от рельса и могут блокировать построение чертежей с рельсовыми сигналами поверх них. Это изменило инвариант для сигнала рельса от всегда подключенного к рельсу, если возможно, только для соединения, если он не обозначен для деконструкции. Большинство частей кода были исправлены правильно, но был один конкретный фрагмент кода, который повторно подключал сигнал, даже если он был отмечен для деконструкции, что сделало внутреннее состояние непоследовательным и сохранение не загружалось до перехода на повторное построение сегментов рельсов был повторно активирован для следующего перехода версии.

Заключение

Итак, теперь вы знаете, почему гораздо больше сбоев при загрузке игр, и вы, надеюсь, ненавидяте меня меньше, потому что теперь вы знаете, что это было сделано ради долгосрочной корректности кода.

И как всегда обсуждайте на нашем форуме


Поделиться

Комментарии: