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

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



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


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

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

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

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

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

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

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

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

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



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


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

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

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

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

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

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


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

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

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

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


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

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

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

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

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



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

Но почему-то переводчики при пруфриде решили заменить «C» на «Z»! Т.е. «Price» (цена за участие в лотерее) внезапно превратилась в «Prize» (приз). И игроки рефлекторно жали кнопку, пока не тратили ВСЮ харду. Ну а что, «приз» же увеличивается с каждой покупкой.



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

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

Самое время вводить хештег #косякинапроде

Комментарии (29)


  1. grcool
    18.07.2018 12:43
    +1

    Все до ужаса знакомо))


  1. Revertis
    18.07.2018 13:32
    -17

    После прочтения возникает одна мысль — как же костылеориентированно у вас всё устроено… Двойные отправки решаются костылями, а не айдишниками пакетов. Prize можно было бы заметить даже слепым тестировщиком. DDoS тоже можно было обычным тестированием заметить, всего лишь если сделать логгирование отправки запросов.


    1. TimsTims
      18.07.2018 13:47
      +8

      Вы конечно же таких ошибок никогда не допускали, и у вас множество успешных аналогичных проектов.


      1. SiliconValleyHobo
        18.07.2018 14:01
        +6

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


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


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


        1. azazelis Автор
          18.07.2018 14:04
          +7

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

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


        1. SirEdvin
          18.07.2018 14:50
          +5

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


          1. Biga
            18.07.2018 15:42
            +10

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


          1. SiliconValleyHobo
            18.07.2018 18:13
            +1

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


            1. SirEdvin
              18.07.2018 20:39
              +1

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


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


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


              1. SiliconValleyHobo
                18.07.2018 21:47

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


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


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


                1. VaalKIA
                  19.07.2018 00:21

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


                1. SirEdvin
                  19.07.2018 11:21
                  +1

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


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


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


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

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


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


        1. MicroSDA
          18.07.2018 17:17
          +1

          Возглас «опять обо....» больше применим к пользователем и как пользователь я могу согласиться с вами, но как разработчик, нет.

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

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


      1. x67
        19.07.2018 00:02

        Но тут автор коммента прав, все действительно как-то глупо выглядит. Такое чувство, будто даже не думали над возможными проблемами. HP на клиенте? в мультиплеерной игре? Аж в 2015 году? А так можно было?

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


        1. algotrader2013
          20.07.2018 00:24

          HP на клиенте? в мультиплеерной игре? Аж в 2015 году?

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

          Пока играет 3 калеки, то никто ломать не будет, а захватив рынок, и переписать «как надо» не пробема.


    1. Vnuchok
      18.07.2018 22:44

      Еретик!)))


  1. Avitale
    18.07.2018 13:40
    +1

    Самое время вводить хештег #косякинапроде

    У каждого программиста найдется несколько историй под такой тэг. :)
    Но каждый такой косяк — это бесценный опыт, как ни крути.


    1. azazelis Автор
      18.07.2018 13:41
      +3

      Вот верный подход, каждый такой косяк — ценный опыт)


      1. JediPhilosopher
        18.07.2018 16:21
        +2

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


        1. TimsTims
          18.07.2018 18:10
          +1

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


  1. gospodinputin
    18.07.2018 16:34
    +2

    Странно, что читеры вобще про эту игру узнали, какие то нескучные танчики.


    1. Year
      19.07.2018 00:12

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


    1. JustPeople
      19.07.2018 06:54

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


  1. oktonion
    18.07.2018 19:00
    +2

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

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


    1. TimsTims
      19.07.2018 18:02

      складывается впечатление что это одна из самых
      Кстати да. Одно дело — скучное офисное ПО для бухгалтеров, которые используют от силы 10% функционала.
      Другое дело — потные задроты, которые любую дыру будут абузить до покраснения :)


  1. skorpix
    19.07.2018 10:40

    #косякинапроде

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

    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/


    1. springimport
      19.07.2018 17:42

      Сам заметил что всегда лучше подчищать заранее то что не нужно. Но бэкапы этого сделать тоже ок.
      А новую версию лучше все же разрабатывать отдельно.


  1. Megard
    19.07.2018 13:18

    А не плохая игра была год назад, удалил после того как они «оптимизировали» управление, теперь ты или двигаешь или управляешь камерой + стреляешь, одновременно нельзя не по фэншую.


  1. 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 разбежались, да так уже и не вернулись.