Блог розробників (FFF#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! »Як нагадування для себе та інших, щоб завжди глибше вникати в причини і ніколи не зупинятися на основному ознаці проблеми.

Як завжди, дайте нам знати, що ви думаєте на нашому форумi


Поделиться

Comments: