company_banner

Как мы перебанили обычных игроков и заDDoSили свои сервера: практическое руководство

    Рассказывать о новых проектах это, конечно, хорошо, но не всегда всё получается, как мы хотим.

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



    Проиграл — получи бан


    Было время, когда нашу игру War Robots из-за недостатков в архитектуре атаковали читеры. Были утилиты, увеличивающие запас здоровья до космических значений, что делало их практически бессмертными. Таск с читерами мы в итоге закрыли, но не сразу.

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

    1. Каждый робот после матча проходил проверку на полученные повреждения.
    2. Если повреждения, полученные роботом, превышали значение его максимального здоровья — то игрок признавался читером, а его аккаунт блокировался.

    Решение было костыльным, но действенным. Проблемы из-за него начались чуть позже, но сначала придется немного рассказать о другом баге и особенности разработки синхронного мобильного PvP.

    Когда у игроков сильно шалит интернет-соединение (а в мобильных играх это нормальная ситуация), при обмене данными между клиентом и серверами могут происходить совершенно волшебные вещи. Запросы от клиентов приходят неполными, не в том порядке или с сильной задержкой. В общем, один серверный баг допускал, что при плохом соединении клиент может присылать результаты боя два или даже три раза подряд. Соответственно, игроки могли получать в 2-3 раза больше наград или случайно потратить на ремонт вдвое больше ресурсов.

    Эту проблему мы решили довольно быстро: профиль-сервер научился игнорировать лишние результаты боя от одного клиента. После успешного тестирования мы зарелизили новую версию.

    Вот тут-то нас и накрыло.

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

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



    Как устроить самому себе DDoS


    Уже писали про эволюцию нашей инфраструктуры серверов, а сейчас вспомнили один случай того времени.

    В конце 2015 года состоялся релиз долгожданной фичи в War Robots — кланов. Когда вышло обновление (а это было поздно вечером), мы открыли шампанское и все было бы хорошо. Но радоваться пришлось недолго — серверам внезапно стало плохо. Оказалось, что мы собственными руками устроили себе DDoS-атаку.

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

    Той же ночью мы запилили флажок (шампанское мы при этом закрыть не успели), который контролировался с профиль-сервера — он полностью блокировал работу Hangar Client API. Игрокам, которые уже вступили в кланы, мы этот флажок оставили включенным, то есть у них все работало, потому что их количество было недостаточно, чтобы заDDoSить сервера.

    В итоге мы начали корректно обрабатывать ответы сервера в игре, а в случае ошибки — увеличивать таймаут на повтор запроса.

    «Бесплатный» рейтинг


    Отдельная история — это когда некачественная реализация встречает человеческий фактор. Только теперь никого не банили, а наоборот раздавали рейтинг налево и направо. Короче, как-то ночью наш мониторинг (а мы мониторим вообще всё) зафиксировал слишком быстрый рост рейтинга игроков.

    Потом выяснилось, что сама реализация подсчета очков в теории позволяла дублировать данные. Но никто бы на это не обратил бы внимания, если бы дежурный админ из-за опечатки в одной букве случайно рестартнул сервер, который не должен был работать. Именно он и начал удваивать игрокам рейтинги.

    Пришлось срочно выпускать фикс и пройтись по базе, чтобы удалить все лишние очки, которые успели начислиться. Чтобы этого не повторилось — на всех серверах мы выпилили старую схему расчета очков и исключили возможность ошибочного запуска сервисов там, где они работать не должны. Надо было так с самого начала сделать, конечно, но было бы слишком скучно.

    Бесценный приз


    С опечаткой был еще один факап, но куда более серьезный.

    Как-то на Хэллоуин мы запускали новую гачу — лотерею. Если кто не знает, гача — это механика получения предмета из нескольких разных случайным образом. В лотерее у игрока был ограниченный видимый набор призов разной ценности. За каждое открытие игрок получал 1 приз, этот приз вынимался из набора, а цена открытия с каждым разом возрастала. Таким образом игрок мог гарантированно скупить все призы лотереи, а счастливчики вынимали самые ценные призы на первых открытиях (и соответственно получали их очень дёшево).

    В общем, потом и кровью мы запилили фичу к ивенту, протестили, выложили. Запускаем, обновляем графики… УРА! Они рванули вверх!.. И одновременно на нас обрушиваются тонны негатива в комьюнити, что мы якобы обманываем своих игроков.

    Уже через полчаса лотерею пришлось выключить. Да, мы действительно обманывали игроков. Но дело было вовсе не в шансах или призах — дело было в одной букве.

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



    А вот как это вышло на проде, когда в результате ряда «улучшений» PRICE (цена за участие в лотерее) внезапно для проектировщика превратилась в PRIZE (приз):



    В то же самое время из-за неразберихи перед релизом и плохого чекинга с каждой отдельной карточки пропала цена за открытие, что всё вместе и запутало игроков.

    И они рефлекторно жали кнопку, пока не тратили всю харду. Ну а что, «приз» же увеличивается с каждой покупкой. И так было на 18 языках. При этом у нас еще и «локали» были на клиенте, поэтому исправить даже одну букву можно было только через хотфикс.

    Ситуацию в итоге развернули, ввели дополнительные чек-поинты, золото игрокам вернули и локали запилили на сервере, чтобы ничего подобного не повторялось.

    Самое время вводить хештег #косякинапроде
    Pixonic 186,18
    Международная компания по разработке мобильных игр
    Поделиться публикацией
    Комментарии 31
      +7
      Все до ужаса знакомо))
        –14
        После прочтения возникает одна мысль — как же костылеориентированно у вас всё устроено… Двойные отправки решаются костылями, а не айдишниками пакетов. Prize можно было бы заметить даже слепым тестировщиком. DDoS тоже можно было обычным тестированием заметить, всего лишь если сделать логгирование отправки запросов.
          +16
          Вы конечно же таких ошибок никогда не допускали, и у вас множество успешных аналогичных проектов.
            +12

            А я поддержу предыдущего оратора. Дело не в ошибках, а (судя по статье) в
            А) Ужасно продуманной архитектуре
            Б) Слабых разработчиках, которые сперва пишут, а потом думают о последствиях/алгоритмической или нагрузочной сложности, если вообще думают.
            С) Упорном нежелании делать вещи правильно и изучать общепринятые решения/консультироваться.


            Мне по молодости-глупости как-то доводилось работать в небольшой конторе с не-топовыми з/п. Вот там я наблюдал похожие перлы от разрабов. Вот серьезно, на контрасте очень заметен уровень разработчиков в таких вопросах.


            Ошибки допускают все, но уровень допускаемых ошибок и устойчивость проектируемой системы к ошибкам у всех разные.

              +9
              На тот момент по факту у нас был стартап. Когда приходилось бежать как можно быстрее и из-за наследуемых косяков, которые возникали из-за изначальных проблем с архитектурой и т.п., поэтому факапов было больше. Когда появилась возможность и время, переделали нормально)

              Тут вопрос в том, делать хорошо, либо очень быстро и иногда с косяками. Как показывает практика, часто одна неделя простоя может быть критической при развитии проекта
                +7
                А я поддержку предыдущего оратора.
                У вас, конечно, каждый проект с
                А) Отлично продуманной архитектурой, которая вовремя обновляется и в ней учтены все возможные косяки
                Б) Каждая задача проходит 40 етапов согласования, бизнес-аналитики сидят и по 2-3 месяца проверяют требования и разработчики, которые пишут задачу несколько недель, зато сразу оптимизированную.
                С) А еще это все идеально фитится в опыт команды и ни у кого нет слепых пятен и все дофига опытные.

                  +13
                  Д) Всех геймдизайнеров перевели на другой проект, чтобы они не придумывали новых фич.
                    0

                    Шутки-шутками, но Б) — это причина, почему компании-гиганты по-прежнему (несмотря на тонны ненависти по этому поводу) дают на собеседовании олимпиадные задачки, вайтбординг, такие ненавистные задачи на биг-О. Тогда боттлнеки будут как минимум замечены перед написанием кода, их выявление уже вросло в подкорку.

                      +2

                      Удивительно, но пункт Б) был написан с тонной сарказма как пример неправильного подхода к разработке.


                      В общем случае биг-О вообще не дает никакого понимания о том, насколько лучше один или другой алгоритм, потому что вы выпускаете из поля зрения настолько много вопросов, включая константы, ограничение на само число N, поведения рантайма языка, скрытые оптимизации операционной системы и так далее. Я уже не говорю о том, что задачи, в которых вам дадут это биг-О считать существуют в исчезающе малом количестве, которое в основном относятся к какой-то bigdata. Ведь именно так умерло доказательное программирование, разве нет?


                      Если бы все было так просто, никто бы не занимался мониторингом, нагрузочным тестированием и прочим. Даже в самом банальном вопросе "квадратичный или линейный алгоритм" иногда можно выбрать квадратичный, потому что максимальное n аж целых 2 или 3, а наглядность кода от этого вырастает.

                        +1

                        Замените биг-О на computational thinking, если уж так придираетесь к формулировкам.


                        Маленьким конторам это все не так важно, как отличное владение фреймворками, библиотеками и языковыми средствами — причины отлично сам ОП расписал парой постов выше. Отсюда и возникают описанные баги.


                        Да, известный парадокс, что нет смысла оставаться в стартапе после его роста тоже в эту тему. Сперва берут и ценят людей, могущих быстро наговнокодить что-то рабочее. Потом приоритет отдается в пользу надежного кода и дальновидных/глубокознающих сотрудников.

                          +2
                          Вы недооцениваете скорость роста компетенции людей, занимающийся программированием, в УСПЕШНЫХ стартапах.
                            +1

                            Опять же таки, касательно "computational thinking" в общем случае крайне сложно сказать о том, какая на самом деле часть системы окажется самой нагруженной. Поэтому в каждой умной книге люди предупреждают насчет "преждевременной оптимизации", когда программист вылизывал перформанс веб приложения 3 месяца, а по факту им пользуется 1.5 анонимуса и прототип, написанный джуном на коленке так же отлично работает с такой нагрузкой.


                            Более того, даже если все пошло идеально и нагрузка большая, потом 100% окажется, что вы забыли про 2-3 важные вещи, которые куда сильнее садят производительность, чем не оптимизированный код, например, кеширование не добавили в нужные места.


                            Если мы говорим про большие компании, то в большом количестве случаев вы будете разрабатывать какой-то внутренний сервис, в котором перформанс вполне может быть не особо важен из-за того, что сервис ближе к пользователю выступает естественным барьером.


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

                            Если что — основной показатель программы это то, то она работает. Всем плевать, насколько она круто написана, если она не работает. И вам никто не мешает говнокодить дальновидно и заранее отмечать места, которые нужно будет отрефакторить или переписать.


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

                              0
                              Опять же таки, касательно «computational thinking» в общем случае крайне сложно сказать о том, какая на самом деле часть системы окажется самой нагруженной

                              Вы утрируете. Безусловно предугадать все нельзя. Но то что ретрай поломавшегося запроса делается без кулдауна — показывает что люди банально не знакомы с устоявшимися хорошими практиками (читай — малоопытные).
                              Поэтому в каждой умной книге люди предупреждают насчет «преждевременной оптимизации»

                              В этом и проявляется senior develoer, что он хорошо разделяет преждевременные оптимизации, и архитектурные решения без которых в продакшн лучше не соваться.
                              Если что — основной показатель программы это то, то она работает

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

                              Насмотрелся я на этих горячих ребят «я не знаю O-notation» но зато я могу побенчить. А потом в продакшене оказывается решение которое на искуственных бенчах Лев Толстой, а на деле вставало колом. Бенчмарки не отменяют здравый смысл и понимание интсрументов которыми пользуешься
                      +2
                      Возглас «опять обо....» больше применим к пользователем и как пользователь я могу согласиться с вами, но как разработчик, нет.
                      Ошибки допускают все, но уровень допускаемых ошибок и устойчивость проектируемой системы к ошибкам у всех разные.

                      Вы правы, их допускают все, и ошибки разные, но вот банальные допускаются как мне кажется чаще в более больших и дорого стающих проектах из-за того что в основное внимание, уделяется более сложным и узким местам. Можно вспомнить недавние брики айфонов из-за простого символа, благо War Robots компенсирует это в той или иной степени. На ум приходит история про «Ахиллесову пяту».
                      +2
                      Но тут автор коммента прав, все действительно как-то глупо выглядит. Такое чувство, будто даже не думали над возможными проблемами. HP на клиенте? в мультиплеерной игре? Аж в 2015 году? А так можно было?

                      Прошу не обижаться разработчиков, но мне кажется, любой мид при написании кода сразу задумывается о возможных проблемах такого рода и они решаются еще на этапе обдумывания архитектуры, а не всплывают на продакшене. Уж точно не грабли сеньора это.
                      Ну и самое забавное, по крайней мере на мобильных платформах, заметил закономерность: чем более развитая система монетизации в игре, тем сильнее она греет телефон и быстрее садит батарейку.
                        +2
                        HP на клиенте? в мультиплеерной игре? Аж в 2015 году?

                        Хороший пример того, что пока одни люди думают о секьюрности, масштабируемости, паттернах, другие не думают, а говнокодят по-черному, и отжимают их рынок.

                        Пока играет 3 калеки, то никто ломать не будет, а захватив рынок, и переписать «как надо» не пробема.
                      0
                      Еретик!)))
                      +3
                      Самое время вводить хештег #косякинапроде

                      У каждого программиста найдется несколько историй под такой тэг. :)
                      Но каждый такой косяк — это бесценный опыт, как ни крути.
                        +3
                        Вот верный подход, каждый такой косяк — ценный опыт)
                          +2
                          И цена бывает очень даже конкретно выраженная в деньгах.
                          Вспоминается история какого-то разработчика, из-за которого контора попала на круглую сумму. Когда он заикнулся насчет ожидаемого увольнения за просчет, директор ему сказал что-то в духе «компания только что по сути вложила в твое образование и опыт миллион долларов, и ты думаешь мы тебя после этого возьмем и уволим?»
                            +2
                            Чуть точнее было так, что технический директор на собрании, защищая сотрудника, сказал: мы только-что вложили в обучение сотрудника Х-миллионов долларов, глупо будет его теперь увольнять.
                        +2
                        Странно, что читеры вобще про эту игру узнали, какие то нескучные танчики.
                          +1
                          War Robots — это, на мой взгляд, неплохой пример игры, вышедшей вовремя. На старте игра выехала на популярности танчиков, при этом она отчасти повторяет геймплей из подзабытой серии Mechwarrior, которая выходит в свет с 1989 года, и у которой есть свои почитатели в мире.
                          Только игра в Mechwarrior — это постоянное страдание из-за нехватки компьютерных ресурсов, нестабильности игры, неудобства управления с клавиатуры и сложности геймплея, которые способен переносить только гик, а в казуальную War Robots на планшетике играть намного удобнее и проще.
                            +1

                            если бы вы, хотя бы раз, реализовывали механику роботов и реалтайм мультиплеера в рамках мобильной платформы у вас бы не возникало таких мыслей )))
                            p.s. игра интересная как не крути что с точки задачи разработки что с точки геймплея, Pixonic'ам респект )

                            +6
                            + просто за КДПВ

                            Очень фееричная история. Вообще из всех постов о разработке игр складывается впечатление что это одна из самых, как бы это назвать… богатых на веселые факапы индустрий. И чем глубже игра, тем больше их.
                              0
                              складывается впечатление что это одна из самых
                              Кстати да. Одно дело — скучное офисное ПО для бухгалтеров, которые используют от силы 10% функционала.
                              Другое дело — потные задроты, которые любую дыру будут абузить до покраснения :)
                              +4
                              #косякинапроде

                              Был один раз косяк, когда на проде удалили половину медиа файлов. А все потому, что:

                              1. Начали разрабатывать новую версию сайта
                              2. Сделали для медиа отдельную папку /beta/media/
                              3. Начали туда заливать много картинок в папку /beta/media/templates/
                              4. Бета вышла из своей беты и решили на /beta/ тестить новые сборки, а на /prod/ прод
                              5. Т.к. в обеих версиях нужна медиа (особенно, чтобы папка /templates/ была и там и там одинаковой), а в папке /beta/ она уже есть, решили, что /prod/media/templates — ссылка на /beta/media/templates
                              6. Прошло полгода, решили отказаться от версии /beta/ но файлы пока не удалять «атовдругчо»
                              7. Прошел еще год и теперь-то пора удалять ненужные файлы.
                              8. Удалили папку /beta/
                                0
                                Сам заметил что всегда лучше подчищать заранее то что не нужно. Но бэкапы этого сделать тоже ок.
                                А новую версию лучше все же разрабатывать отдельно.
                                0
                                А не плохая игра была год назад, удалил после того как они «оптимизировали» управление, теперь ты или двигаешь или управляешь камерой + стреляешь, одновременно нельзя не по фэншую.
                                  +2
                                  Геймдев-истории! Люблю такое.

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

                                  С рейсингом вышла история очень поганая в одном месте. Он был изначально чисто сингловый, и всё было хорошо. Но тут решили приделать всякий бэкэнд, и после некоторого раздумья выбрали для этого контору 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 разбежались, да так уже и не вернулись.
                                    0
                                    Лотерея у вас сейчас странная, ребята. Что в этом релизе, что в прошлых двух. Призы тупо выдаются начиная с самого дешевого и до самого ценного по порядку. Получается просто покупка вещей за игровые жетоны. Хотя изредка можно перескочить на один шаг вперед и тогда считай выиграл.

                                    Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                    Самое читаемое