В общем, начали тут вспоминать факапы из прошлого, когда решение одной проблемы прибавляло новых, увлеклись и решили поделиться парочкой. Как забанить невиновных игроков, заддосить собственные сервера, ошибиться в одной букве и словить тонны негатива от пользователей — вот это всё, как мы любим.
Проиграл — получи бан
Было время, когда нашу игру War Robots из-за недостатков в архитектуре атаковали читеры. Были утилиты, увеличивающие запас здоровья до космических значений, что делало их практически бессмертными. Таск с читерами мы в итоге закрыли, но не сразу.
Сначала мы хотели решить проблему красиво на техническом уровне: заблокировать возможность изменять на клиенте параметры робота. Сделать нам этого не удалось (потом, конечно, нашли способ). И тогда первым работающим решением стал банхаммер, который вычислял читеров по простой схеме:
- Каждый робот после матча проходил проверку на полученные повреждения.
- Если повреждения, полученные роботом, превышали значение его максимального здоровья — то игрок признавался читером, а его аккаунт блокировался.
Решение было костыльным, но действенным. Проблемы из-за него начались чуть позже, но сначала придется немного рассказать о другом баге и особенности разработки синхронного мобильного PvP.
Когда у игроков сильно шалит интернет-соединение (а в мобильных играх это нормальная ситуация), при обмене данными между клиентом и серверами могут происходить совершенно волшебные вещи. Запросы от клиентов приходят неполными, не в том порядке или с сильной задержкой. В общем, один серверный баг допускал, что при плохом соединении клиент может присылать результаты боя два или даже три раза подряд. Соответственно, игроки могли получать в 2-3 раза больше наград или случайно потратить на ремонт вдвое больше ресурсов.
Эту проблему мы решили довольно быстро: профиль-сервер научился игнорировать лишние результаты боя от одного клиента. После успешного тестирования мы зарелизили новую версию.
Вот тут-то нас и накрыло.
Целую кучу игроков ежедневно начало банить банхаммером, про который мы благополучно забыли, т.к. проблема неубиваемых роботов с бесконечным запасом здоровья была далеко в прошлом. Оказалось, что как только клиент игрока присылал на сервер результаты одного боя в двойном количестве, банхаммер воспринимал это так, что каждый погибший робот был убит дважды — т.е. получил урон, вдвое превосходящий его здоровье. И после каждого боя игроки улетали в бан пачками.
Всех, конечно, разбанили и даже компенсацию выплатили, но ситуация так себе, не позитивная точно.
Как устроить самому себе DDoS
Уже писали про эволюцию нашей инфраструктуры серверов, а сейчас вспомнили один случай того времени.
В конце 2015 года состоялся релиз долгожданной фичи в War Robots — кланов. Когда вышло обновление (а это было поздно вечером), мы открыли шампанское и все было бы хорошо. Но радоваться пришлось недолго — серверам внезапно стало плохо. Оказалось, что мы собственными руками устроили себе DDoS-атаку.
Как? Очень просто. Клиент на экране результатов боя в попытках получить информацию о кланах игроков делал слишком много запросов. И когда сервер отвечал «отстань, ошибка», клиент без какого-либо тайм-аута возвращался к серверу.
Той же ночью мы запилили флажок (шампанское мы при этом закрыть не успели), который контролировался с профиль-сервера — он полностью блокировал работу Hangar Client API. Игрокам, которые уже вступили в кланы, мы этот флажок оставили включенным, то есть у них все работало, потому что их количество было недостаточно, чтобы заDDoSить сервера.
В итоге мы начали корректно обрабатывать ответы сервера в игре, а в случае ошибки — увеличивать таймаут на повтор запроса.
«Бесплатный» рейтинг
Отдельная история — это когда некачественная реализация встречает человеческий фактор. Только теперь никого не банили, а наоборот раздавали рейтинг налево и направо. Короче, как-то ночью наш мониторинг (а мы мониторим вообще всё) зафиксировал слишком быстрый рост рейтинга игроков.
Потом выяснилось, что сама реализация подсчета очков в теории позволяла дублировать данные. Но никто бы на это не обратил бы внимания, если бы дежурный админ из-за опечатки в одной букве случай не рестартнул сервер, который не должен был работать. Именно он и начал удваивать игрокам рейтинги.
Пришлось срочно выпускать фикс и пройтись по базе, чтобы удалить все лишние очки, которые успели начислиться. Чтобы этого не повторилось — на всех серверах мы выпилили старую схему расчета очков и исключили возможность ошибочного запуска сервисов там, где они работать не должны. Надо было так с самого начала сделать, конечно, но было бы слишком скучно.
Бесценный приз
С опечаткой был еще один факап, но куда более серьезный.
Как-то на Хэллоуин мы запускали новую гачу — лотерею. Если кто не знает, гача — это механика получения предмета из нескольких разных случайным образом. В лотерее у игрока был ограниченный видимый набор призов разной ценности. За каждое открытие игрок получал 1 приз, этот приз вынимался из набора, а цена открытия с каждым разом возрастала. Таким образом игрок мог гарантированно скупить все призы лотереи, а счастливчики вынимали самые ценные призы на первых открытиях (и соответственно получали их очень дёшево).
В общем, потом и кровью мы запилили фичу к ивенту, протестили, выложили. Запускаем, обновляем графики… УРА! Они рванули вверх!.. И одновременно на нас обрушиваются тонны негатива в комьюнити, что мы якобы обманываем своих игроков.
Уже через полчаса лотерею пришлось выключить. Да, мы действительно обманывали игроков. Но дело было вовсе не в шансах или призах — дело было в одной букве.
В интерфейсе лотерии указана стоимость текущего открытия (та, которая возрастала с каждым разом), например, PRICE: 100 Gold. Специально выделена на скриншоте:
Но это уже исправленный интерфейс, а на проде тогда было по-другому. В общем, перед релизом фичи мы отправляем новые тексты на перевод, получаем пруфрид и варианты на 18 языках.
Но почему-то переводчики при пруфриде решили заменить «C» на «Z»! Т.е. «Price» (цена за участие в лотерее) внезапно превратилась в «Prize» (приз). И игроки рефлекторно жали кнопку, пока не тратили ВСЮ харду. Ну а что, «приз» же увеличивается с каждой покупкой.
И так было на 18 языках. При этом «локали» у нас были на клиенте, поэтому исправить одну букву можно было только через хотфикс.
Золото в итоге мы вернули игрокам. А чтобы подобного не повторилось, пришлось пилить локали на сервере, чтобы ошибка в одной букве больше не рушила игрокам праздники, а наши руки не заставляла судорожно работать с утроенной скоростью.
Самое время вводить хештег #косякинапроде
Комментарии (29)
Revertis
18.07.2018 13:32-17После прочтения возникает одна мысль — как же костылеориентированно у вас всё устроено… Двойные отправки решаются костылями, а не айдишниками пакетов. Prize можно было бы заметить даже слепым тестировщиком. DDoS тоже можно было обычным тестированием заметить, всего лишь если сделать логгирование отправки запросов.
TimsTims
18.07.2018 13:47+8Вы конечно же таких ошибок никогда не допускали, и у вас множество успешных аналогичных проектов.
SiliconValleyHobo
18.07.2018 14:01+6А я поддержу предыдущего оратора. Дело не в ошибках, а (судя по статье) в
А) Ужасно продуманной архитектуре
Б) Слабых разработчиках, которые сперва пишут, а потом думают о последствиях/алгоритмической или нагрузочной сложности, если вообще думают.
С) Упорном нежелании делать вещи правильно и изучать общепринятые решения/консультироваться.
Мне по молодости-глупости как-то доводилось работать в небольшой конторе с не-топовыми з/п. Вот там я наблюдал похожие перлы от разрабов. Вот серьезно, на контрасте очень заметен уровень разработчиков в таких вопросах.
Ошибки допускают все, но уровень допускаемых ошибок и устойчивость проектируемой системы к ошибкам у всех разные.
azazelis Автор
18.07.2018 14:04+7На тот момент по факту у нас был стартап. Когда приходилось бежать как можно быстрее и из-за наследуемых косяков, которые возникали из-за изначальных проблем с архитектурой и т.п., поэтому факапов было больше. Когда появилась возможность и время, переделали нормально)
Тут вопрос в том, делать хорошо, либо очень быстро и иногда с косяками. Как показывает практика, часто одна неделя простоя может быть критической при развитии проекта
SirEdvin
18.07.2018 14:50+5А я поддержку предыдущего оратора.
У вас, конечно, каждый проект с
А) Отлично продуманной архитектурой, которая вовремя обновляется и в ней учтены все возможные косяки
Б) Каждая задача проходит 40 етапов согласования, бизнес-аналитики сидят и по 2-3 месяца проверяют требования и разработчики, которые пишут задачу несколько недель, зато сразу оптимизированную.
С) А еще это все идеально фитится в опыт команды и ни у кого нет слепых пятен и все дофига опытные.
Biga
18.07.2018 15:42+10Д) Всех геймдизайнеров перевели на другой проект, чтобы они не придумывали новых фич.
SiliconValleyHobo
18.07.2018 18:13+1Шутки-шутками, но Б) — это причина, почему компании-гиганты по-прежнему (несмотря на тонны ненависти по этому поводу) дают на собеседовании олимпиадные задачки, вайтбординг, такие ненавистные задачи на биг-О. Тогда боттлнеки будут как минимум замечены перед написанием кода, их выявление уже вросло в подкорку.
SirEdvin
18.07.2018 20:39+1Удивительно, но пункт Б) был написан с тонной сарказма как пример неправильного подхода к разработке.
В общем случае биг-О вообще не дает никакого понимания о том, насколько лучше один или другой алгоритм, потому что вы выпускаете из поля зрения настолько много вопросов, включая константы, ограничение на само число N, поведения рантайма языка, скрытые оптимизации операционной системы и так далее. Я уже не говорю о том, что задачи, в которых вам дадут это биг-О считать существуют в исчезающе малом количестве, которое в основном относятся к какой-то bigdata. Ведь именно так умерло доказательное программирование, разве нет?
Если бы все было так просто, никто бы не занимался мониторингом, нагрузочным тестированием и прочим. Даже в самом банальном вопросе "квадратичный или линейный алгоритм" иногда можно выбрать квадратичный, потому что максимальное n аж целых 2 или 3, а наглядность кода от этого вырастает.
SiliconValleyHobo
18.07.2018 21:47Замените биг-О на computational thinking, если уж так придираетесь к формулировкам.
Маленьким конторам это все не так важно, как отличное владение фреймворками, библиотеками и языковыми средствами — причины отлично сам ОП расписал парой постов выше. Отсюда и возникают описанные баги.
Да, известный парадокс, что нет смысла оставаться в стартапе после его роста тоже в эту тему. Сперва берут и ценят людей, могущих быстро наговнокодить что-то рабочее. Потом приоритет отдается в пользу надежного кода и дальновидных/глубокознающих сотрудников.
VaalKIA
19.07.2018 00:21Вы недооцениваете скорость роста компетенции людей, занимающийся программированием, в УСПЕШНЫХ стартапах.
SirEdvin
19.07.2018 11:21+1Опять же таки, касательно "computational thinking" в общем случае крайне сложно сказать о том, какая на самом деле часть системы окажется самой нагруженной. Поэтому в каждой умной книге люди предупреждают насчет "преждевременной оптимизации", когда программист вылизывал перформанс веб приложения 3 месяца, а по факту им пользуется 1.5 анонимуса и прототип, написанный джуном на коленке так же отлично работает с такой нагрузкой.
Более того, даже если все пошло идеально и нагрузка большая, потом 100% окажется, что вы забыли про 2-3 важные вещи, которые куда сильнее садят производительность, чем не оптимизированный код, например, кеширование не добавили в нужные места.
Если мы говорим про большие компании, то в большом количестве случаев вы будете разрабатывать какой-то внутренний сервис, в котором перформанс вполне может быть не особо важен из-за того, что сервис ближе к пользователю выступает естественным барьером.
Сперва берут и ценят людей, могущих быстро наговнокодить что-то рабочее. Потом приоритет отдается в пользу надежного кода и дальновидных/глубокознающих сотрудников.
Если что — основной показатель программы это то, то она работает. Всем плевать, насколько она круто написана, если она не работает. И вам никто не мешает говнокодить дальновидно и заранее отмечать места, которые нужно будет отрефакторить или переписать.
Ну и напоследок, хочешь не хочешь, а вместо прикидывания на глаз алгоритмической сложности все равно надо будет бенчить код, потому что правильно прикинуть сложность кода дай бог топ-топ программисты в кремниевой долине смогут. А бенчить можно и без знаний О-нотации и сложности алгоритмов.
MicroSDA
18.07.2018 17:17+1Возглас «опять обо....» больше применим к пользователем и как пользователь я могу согласиться с вами, но как разработчик, нет.
Ошибки допускают все, но уровень допускаемых ошибок и устойчивость проектируемой системы к ошибкам у всех разные.
Вы правы, их допускают все, и ошибки разные, но вот банальные допускаются как мне кажется чаще в более больших и дорого стающих проектах из-за того что в основное внимание, уделяется более сложным и узким местам. Можно вспомнить недавние брики айфонов из-за простого символа, благо War Robots компенсирует это в той или иной степени. На ум приходит история про «Ахиллесову пяту».
x67
19.07.2018 00:02Но тут автор коммента прав, все действительно как-то глупо выглядит. Такое чувство, будто даже не думали над возможными проблемами. HP на клиенте? в мультиплеерной игре? Аж в 2015 году? А так можно было?
Прошу не обижаться разработчиков, но мне кажется, любой мид при написании кода сразу задумывается о возможных проблемах такого рода и они решаются еще на этапе обдумывания архитектуры, а не всплывают на продакшене. Уж точно не грабли сеньора это.
Ну и самое забавное, по крайней мере на мобильных платформах, заметил закономерность: чем более развитая система монетизации в игре, тем сильнее она греет телефон и быстрее садит батарейку.algotrader2013
20.07.2018 00:24HP на клиенте? в мультиплеерной игре? Аж в 2015 году?
Хороший пример того, что пока одни люди думают о секьюрности, масштабируемости, паттернах, другие не думают, а говнокодят по-черному, и отжимают их рынок.
Пока играет 3 калеки, то никто ломать не будет, а захватив рынок, и переписать «как надо» не пробема.
Avitale
18.07.2018 13:40+1Самое время вводить хештег #косякинапроде
У каждого программиста найдется несколько историй под такой тэг. :)
Но каждый такой косяк — это бесценный опыт, как ни крути.azazelis Автор
18.07.2018 13:41+3Вот верный подход, каждый такой косяк — ценный опыт)
JediPhilosopher
18.07.2018 16:21+2И цена бывает очень даже конкретно выраженная в деньгах.
Вспоминается история какого-то разработчика, из-за которого контора попала на круглую сумму. Когда он заикнулся насчет ожидаемого увольнения за просчет, директор ему сказал что-то в духе «компания только что по сути вложила в твое образование и опыт миллион долларов, и ты думаешь мы тебя после этого возьмем и уволим?»TimsTims
18.07.2018 18:10+1Чуть точнее было так, что технический директор на собрании, защищая сотрудника, сказал: мы только-что вложили в обучение сотрудника Х-миллионов долларов, глупо будет его теперь увольнять.
gospodinputin
18.07.2018 16:34+2Странно, что читеры вобще про эту игру узнали, какие то нескучные танчики.
Year
19.07.2018 00:12War Robots — это, на мой взгляд, неплохой пример игры, вышедшей вовремя. На старте игра выехала на популярности танчиков, при этом она отчасти повторяет геймплей из подзабытой серии Mechwarrior, которая выходит в свет с 1989 года, и у которой есть свои почитатели в мире.
Только игра в Mechwarrior — это постоянное страдание из-за нехватки компьютерных ресурсов, нестабильности игры, неудобства управления с клавиатуры и сложности геймплея, которые способен переносить только гик, а в казуальную War Robots на планшетике играть намного удобнее и проще.
JustPeople
19.07.2018 06:54если бы вы, хотя бы раз, реализовывали механику роботов и реалтайм мультиплеера в рамках мобильной платформы у вас бы не возникало таких мыслей )))
p.s. игра интересная как не крути что с точки задачи разработки что с точки геймплея, Pixonic'ам респект )
oktonion
18.07.2018 19:00+2+ просто за КДПВ
Очень фееричная история. Вообще из всех постов о разработке игр складывается впечатление что это одна из самых, как бы это назвать… богатых на веселые факапы индустрий. И чем глубже игра, тем больше их.TimsTims
19.07.2018 18:02складывается впечатление что это одна из самых
Кстати да. Одно дело — скучное офисное ПО для бухгалтеров, которые используют от силы 10% функционала.
Другое дело — потные задроты, которые любую дыру будут абузить до покраснения :)
skorpix
19.07.2018 10:40#косякинапроде
Был один раз косяк, когда на проде удалили половину медиа файлов. А все потому, что:
- Начали разрабатывать новую версию сайта
- Сделали для медиа отдельную папку /beta/media/
- Начали туда заливать много картинок в папку /beta/media/templates/
- Бета вышла из своей беты и решили на /beta/ тестить новые сборки, а на /prod/ прод
- Т.к. в обеих версиях нужна медиа (особенно, чтобы папка /templates/ была и там и там одинаковой), а в папке /beta/ она уже есть, решили, что /prod/media/templates — ссылка на /beta/media/templates
- Прошло полгода, решили отказаться от версии /beta/ но файлы пока не удалять «атовдругчо»
- Прошел еще год и теперь-то пора удалять ненужные файлы.
- Удалили папку /beta/
springimport
19.07.2018 17:42Сам заметил что всегда лучше подчищать заранее то что не нужно. Но бэкапы этого сделать тоже ок.
А новую версию лучше все же разрабатывать отдельно.
Megard
19.07.2018 13:18А не плохая игра была год назад, удалил после того как они «оптимизировали» управление, теперь ты или двигаешь или управляешь камерой + стреляешь, одновременно нельзя не по фэншую.
MaxEdZX
19.07.2018 20:27+1Геймдев-истории! Люблю такое.
Мы чуть случайно не стали вашими конкурентами, но начальство прототип про войну роботов зарезало, приказав сосредоточится на дрэг-рейсинге.
С рейсингом вышла история очень поганая в одном месте. Он был изначально чисто сингловый, и всё было хорошо. Но тут решили приделать всякий бэкэнд, и после некоторого раздумья выбрали для этого контору GameSparks, чтобы самим писать поменьше. Какой ошибкой это оказалось! Их C++ SDK начал вызывать у меня лёгкие вопросы ещё при первом знакомстве, когда я увидел, что они удаляют первый элемент из вектора через последовательность std::reverse + std::pop_back + std::reverse. Поправив в их SDK несколько подобных косяков, всё-таки решили им пользоваться.
И вот приходит день и час, и мы выкатываем очередную версию на iOS. Выкатили. А она вешается на стартовом экране намертво у некоторых игроков. Подлость в том, что на всех имеющихся устройствах баг не воспроизводился вообще никак, но зато — воспроизводился на самом свежем по тем временам iPhone 6. Который, по счастью, обнаружился у инвестора, и мы у него его взяли на время отладки.
Дело, конечно же, оказалось в race condition. И что самое прекрасное — не у нас. А в этом самом GameSparks SDK, но даже и не в нём, а в его open source зависимости. Которую авторы GameSparks допилили кривыми добрыми руками (добавив поддержку HTTPS при помощи OpenSSL), конечно же, у себя в форке, не послав в исходный проект никаких пулл-риквестов, и не пройдя никаких проверок сообществом. А в результате у них там, кажется, звался pthread_join на хэндл уже умершего треда, и на этом всё зависало навсегда (очевидно, если тред успел умереть до того, что на более медленных устройствах не происходило).
Фикс состоял насколько я помню что-то около из одной строчки или двух, но пока мы обнаружили, что происходит что-то ненормальное, пока оторвались от других платформ (для нас тогда в приоритете был Windows Phone, т.к. приносил больше всего денег — там у нас не было конкурентов) — все пользователи, какие у нас были на iOS разбежались, да так уже и не вернулись.
grcool
Все до ужаса знакомо))