Пятничные факты #296 – Всевозможные ошибки

Поделиться

опубликовали Klonan, Rseding

Автомобили / Танки помнят свой цвет Klonan

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

Так что теперь вы не забудете, на каком транспортном средстве вы ехали, и можете предупредить всех остальных на сервере: «Розовый танк – мой».

Всевозможные ошибки Rseding

Как бы неинтересно это ни было; большинство ошибок скучны и обычно связаны с отсутствующим кодом. Кто-то забыл реализовать часть новой функции, забыл, что может произойти какая-то ситуация, забыл проверить нулевое значение. Редко интересные вещи обнаруживаются там, где все работает, но не так, как мы этого хотим.

Производительность: это всегда не то, что вы думаете

Недавно у нас был отчет об ошибке, когда  игра с модами зависала на минуту без видимой причины, а затем продолжала, как будто ничего не было. Это была сильно модифицированная игра, и моя первая реакция заключалась в том, чтобы обвинить моды в том, что они что-то делают неоптимизированным образом. Но я должен был проверить это, чтобы выяснить, какой мод вызывал проблему. После воспроизведения … Мне напомнили (снова), что это (почти) всегда не то, что вы думаете.

Когда игрок управляет поездом, игра не знает, в каком направлении будет двигаться игрок (прямо, налево или направо). Таким образом, когда поезд движется, игра пересекает потенциальные рельсы перед поездом и просит открыть все найденные ворота на тот случай, если поезд проедет через них. Эта логика была очень проста: получить расстояние до рельса, чтобы поезд остановился на своей текущей скорости и «шел» по рельсам, пока он не превысил это расстояние. Пока он шел, сказать открыть любые ворота на любом из этих рельсов.

Эта логика является «правильной» в том смысле, что она делает то, что должна была сделать: открыть все ворота, через которые поезд может проехать. Чего она не учитывала, так это рельсовой системы, в которой все зацикливалось на 5-10 раз на каждом перекрестке. Сложность времени для алгоритма, который он использовал, была O (N ^ 2). Это нормально, когда N маленькое. Тем не менее, в этом файле сохранения, с этой железнодорожной сетью и с этими поездами с модов (с бонусом скорости 2500% от модифицированного топлива ) это означало, что N в конечном итоге составило 75 755. Это, как выясняется, было тем что замедляло игру.

Интересно, что хотя алгоритм был рекурсивным, он не переполнял стек. Старый алгоритм выполнял «открытые ворота на этой железной дороге» 5 755 573 057 раз. Многие из тех просьб открыть ворота дублировались но алгоритм не заботился об этом. В общей сложности ему потребовалось 57 секунд, чтобы выполнить всю логику – все еще невероятно быстро для того, что он делал (1,6 миллиона рельсов за такт игры).

После некоторого размышления об этом; Я смог повторно реализовать алгоритм в худшем случае за O (N), который в итоге выполнил логику «открытые ворота на этой жд» 42 913 раз и занял 0,009 секунды.

Сбой при определении null? Добавить проверку null

Я люблю эту фразу. Это и правильно, и неправильно одновременно. Я поместил это прямо рядом с “Crashing from an exception? Just try-catch”. Это такая простая ловушка: исправить симптом вместо причины.

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

Вылет был правильный: игра исчерпывала ОЗУ, и когда это происходило, она вылетала. Но почему?

  • Игре не удалось выделить память, когда она пыталась создать дым в печи
  • Потому что он пытался сделать 4’294’967’000 ~ (4 миллиарда) дыма
  • Поскольку небольшое отрицательное целое число со знаком преобразовывалось в unsigned

Очевидно, что игра не должна была производить 4 миллиарда дымов. Итак, видя проблему, мое первое исправление было следующим: «Приведение небольшого отрицательного целого числа со знаком к беззнаковому дает 4 миллиарда? Если оно отрицательное, я просто верну 0, поскольку отрицательное количество дыма не имеет смысла». Я собирался совершить это исправление, когда более умная часть моего мозга сказала: «Это не имеет смысла, с чего бы этот код когда-либо пытался создать отрицательное количество дыма?» Так что я продолжал … почему?

  • … Потому что логика для создания этих целых чисел со знаком была “(прогресс цикла …) – (прогресс последнего цикла …)” (цикл был <последний цикл … это никогда не должно быть возможным)
  • Поскольку печь достигла отрицательного прогресса в сжигании топлива (отрицательный прогресс не должен быть возможен)
  • Потому что «оставшееся количество топлива от этого предмета для сжигания» было отрицательным (отрицательные значения топлива недействительны, и игра даже не достигнет главного меню, если какой-то мод попытается установить его)
  • Потому что API модов не помешало модам делать: entity.burner.remaining_burning_fuel = -1 И игра не удаляла должным образом «оставшееся количество топлива для сжигания», когда сжигаемый предмет был удален из-за миграции модов/удаления.

Итак, я все еще повторяю фразу: «Сбой при определении null? Просто добавьте проверку null!» как напоминание для себя и других, чтобы всегда глубже вникать в причины и никогда не останавливаться на основном признаке проблемы.

Как всегда, дайте нам знать, что вы думаете на нашем форуме.


Поделиться

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