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

Влезаем в клиент игры


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

Основной игровой скрипт лежит в файле main_out.js.
Код там конечно же обфусцирован и всячески пытается не давать себя запускать откуда не следует:

if ("agar.io" != h.location.hostname && "localhost" != h.location.hostname && "10.10.2.13" != h.location.hostname) h.location = "http://agar.io/";

Развернув файл в читаемый вид через дебаггер Хрома, встал вопрос: каким образом вклиниться в логику игры?

Вначале я решил создать локальную копию файлов и соединяться с сервером, отключив в браузере проверку на кроссдомен:

#файлы игры
/css/bootstrap.min.css
/js/jquery.js
/index.html
/main_out.js
/quadtree.js

#запуск Хрома без same origin policy
chrome.exe --disable-web-security

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

Подменяем файлы по урлу


Рабочим решением стала загрузка реального игрового клиента, но подмена для браузера нужных файлов на свои. Для этого устанавливаем замечательную программу Fiddler Web Debugger и указываем нужные пути в табе AutoResponder:



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

Пытаемся обмануть сервер


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

Сервер в свою очередь не присылает клиенту «лишних» для него данных. Например, когда я увеличил масштаб игровой карты, то сервер все равно присылал лишь то окно объектов, которое я должен был видеть в рамках своей клетки:



Казалось бы все пути закрыты: сервер не доверяет клиентам никакой важной информации и всё просчитывает самостоятельно.

Но тогда можно обмануть сервер в рамках его правил: создать стаю ботов, которые постоянно будут жертвовать собой, увеличивая мою массу. Но как же ботам находить мою клетку на карте? Выручило само API сервера: если постоянно отправлять ему например координаты (0, 0), то игровая клетка будет всегда следовать в эту часть карты без остановки, пока не достигнет цели. Вместо нулей надо всего лишь отправлять ботам мои текущие координаты и они сами будут приходить ко мне на ужин!

Пишем ботов в текущем окне


Код клиента одновременно получает данные и перерисовывает объекты на экране. Можно было бы открыть 20 табов, управляемых ботами и один мой игровой таб. Но тогда надо было бы как-то передавать мои координаты в соседние табы. Плюс рисование каждого таба тормозило бы весь браузер (я пробовал — так и есть). Поэтому было решено создавать новые игровые сессии прямо в текущем табе, но выключить для них связь с отображением:

//запускаем новые копии игры
var isBot = true;
for (i = 0; i < botsCount; i++) {
	//нужно делать паузу перед новым ботом,
	//чтобы сервер не отклонил слишком частые соединения
	setTimeout(function(){
		game(window, r, isBot, botsUrl, M);
	}, 500);
}

//не даем эти копиям рисовать на экране
function paint() {
	if(bot) return;
	//...
}

Так же нужно было дописать код, чтобы при смерти бота, он автоматически начинал новую сессию.

Результаты работы


Боты создаются. И находят меня на карте!



Однако, все не так радужно.

Во-первых, сервер раскидывает игроков по игровым комнатам. Поэтому со мной на карту из 50 ботов попадают 2-3. Остальные «играют» в других комнатах, следуя по координатам из соседней Вселенной.

Во-вторых, ботов может съесть кто-то другой! Поэтому им удается придти ко мне где-то пару раз в минуту.

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

Выводы


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

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

Я же своей маленькой цели достиг:


Может быть благодаря ботам, может быть мне повезло самому.

[ Итоговый код клиента ]

Спасибо за внимание и удачи в игре!

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


  1. Reeze
    10.05.2015 20:40
    -2

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


    1. edolganov Автор
      10.05.2015 20:42
      +10

      Зависит. Можно подкрадываться и съедать конкурентов делением. Можно кидать приманки, можно растить зеленые растения, чтобы они делились и разрывали бОльшего конкурента на части…
      Шахматы прям :)


      1. 5angel
        10.05.2015 22:22
        +5

        Да. На самом деле, успех в игре зависит прежде всего от умений игрока, а уже потом от удачи.

        Для того, чтобы стабильно выбиваться в лидеры нежно освоить несколько приемов:
        1) рассчитывать свою массу и массу противника, чтобы при делении не оказаться в патовой ситуации и не быть съеденным;
        2) разбивать лидеров в с помощью зубастых клеток;
        3) обмениваться массой между поделившимися клетками;
        4) ретироваться делением в случае угрозы со стороны лидеров;


      1. wrewolf
        19.05.2015 11:12

        их можно растить?


        1. sferrka
          19.05.2015 11:20

          Да, стреляя в них.


        1. edolganov Автор
          19.05.2015 11:21

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


          1. wrewolf
            19.05.2015 11:55

            вот оно как )


  1. abby
    10.05.2015 21:21
    +1

    Круто, я тоже пользовался fiddler'ом, но хотел немного более интеллектуального бота. Поделюсь своей наработкой немного деобфусцированного скипта, правда, одной из предыдущих версий, gist.github.com/abby-sergz/65aad7682de388f14ee2.


    1. edolganov Автор
      10.05.2015 21:29

      Круто! Спасибо!
      Уже есть достижения в интеллекте бота?


      1. abby
        10.05.2015 21:43

        Достижений пока нет, основная причина — мало времени :)


  1. alhimik45
    10.05.2015 21:33

    Во-первых, сервер раскидывает игроков по игровым комнатам. Поэтому со мной на карту из 50 ботов попадают 2-3. Остальные «играют» в других комнатах, следуя по координатам из соседней Вселенной.

    Можно переконнектиться к нужному серверу, зная его IP: в консоли после выбора региона будет написан ip-адрес, потом можно коннектиться к нему:
    connect("ws://213.219.39.46:443"); //нужный ip сервера
    

    Правда, наверное, нужно будет что-то подправить чтобы это всё работало на одной странице.


    1. edolganov Автор
      10.05.2015 21:40

      Если я не ошибся, то сервер на одном ip содержит много игровых комнат. Потому что я подставляю один и тот же ip для всех ботов. Но ко мне попадает лишь часть. Другие крутятся с другими игроками.
      В итоге в комнате играет 15-20 игроков, на сервер может и 5000.


      1. alhimik45
        10.05.2015 21:50

        Хм, точно. Тестил со вкладками браузера. Если подключаться довольно быстро, то попадаю в одну комнату, если через некоторое время, то уже кидает в другую.


        1. astal
          10.05.2015 21:51

          Комнат там действительно довольно много m.agar.io/info


          1. MaximChistov
            10.05.2015 22:09

            в US-Fremont всего 3 комнаты на сервак)


  1. astal
    10.05.2015 21:34
    +2

    Можно и проще. Перед загрузкой удаляем main_out из страницы и подгружаем свою копию. Пример для GM или аналогов — pastebin.com/7ZhB5cVD


    1. edolganov Автор
      10.05.2015 21:41

      Спасибо!


    1. Scorpi
      10.05.2015 22:02

      К сожалению работает не для всех браузеров.
      «beforescriptexecute» был убран из Chrome.


  1. MaximChistov
    10.05.2015 21:35

    (del)


    1. akke
      11.05.2015 02:04
      -3

      В треде на reddit уже всё давно обсудили, и даже разработчик ответил: www.reddit.com/r/Agario/comments/34z3zp

      Да и на youtube, к слову, такие же боты-кормильцы 8 дней как выложены: www.youtube.com/watch?v=XyLWCdnff2A


  1. DnAp
    10.05.2015 22:43
    +4

    Я пошел по другому пути. Ботов не стал делать, а расковырял оригинальную js и сделал пачку улучшений код на githab


    1. sferrka
      12.05.2015 03:28
      +1

      Круто, еще бы границы поля сделать видимыми и флаги вернуть)


      1. DnAp
        13.05.2015 00:37

        Ага про поля как доберусь, сделаю. А флаги стран работают даже с «Enemy Types Hack», сейчас проверил.


  1. vintage
    11.05.2015 15:12
    +13

    Я пошёл дальше и вообще исключил игрока оставив лишь ботов, которые кооперируются для выживания.

    Свой код я инъектирую с помощью отладчика. Достаточно остановить его в любой внутренней функции и мы имеем полный доступ ко внутренним переменным. Так что можно через консоль создать замыкание, вызывающееся по таймеру, и можно творить что угодно.

    Инфа о своих клетках находится в хэше «m». Инфа обо всех остальных клетках лежит в словаре «v». Этого уже достаточно для игры. Пробегаемся в цикле по своим клеткам и по чужим и складываем вычисленные для пар «я — не я» ускорения. Потом дёргаем onmousemove, чтобы сообщить движку в какую сторону мы хотим ускориться.

    Все чужие клетки делятся на следующие группы:

    1. Вирусы — зелёные пассивные клетки, которые могут взрывать остальных. У них стоит флаг isVirus. От них отталкиваемся по обратно квадратичному закону.

    2. Друзья — дружеские боты, действующие всоседних вкладках. Их определяем по префиксу в имени. К ним притягиваемся с константной силой. Это позволяет им находить друг друга даже если отреспаунились в разных конца карты.

    3. Враги — клетки, которые больше максимального размера, что мы можем съесть. От них отталкиваемся как отвирусов, но с другим коэффициентом.

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

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

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

    Чтобы отреспауниться, достаточно вызвать функцию setNick(string). Я её вызываю просто всегда при вызове моего замыкания.

    Фото рекорда:
    image

    Видео с четырьмя ботами:


    Код бота без констант - подберите свои значения :-)
    window.canvas = document.getElementById('canvas')
    
    if( window.botovod ) clearInterval( botovod )
    
    window.botovod = setInterval(function () {
        setNick(nname)
    
        var mys = m
    
        var others = Object.keys(v).map(function (k) {
            return v[k]
        })
    
        var dist = function (one, two) {
            return Math.sqrt( Math.pow(one.x - two.x, 2) + Math.pow(one.y - two.y, 2) )
        }
    
        var aX = 0
        var aY = 0
    
        mys.forEach( function(my) {
            others.forEach(function (o) {
                if( my === o ) return
                var od = dist(my, o)
                if (o.isVirus) { // virus
                    var f = - localStorage.antivir / Math.pow(od, 2)
                } else if (o.name.indexOf(pfix) === 0) { // friend
                    var fac = my.size / o.size
                    if( fac > 1 ) fac = 1/fac
                    if (fac < 0.9) {
                        var f = localStorage.friendly
                    } else {
                        var f = 0
                    }
                } else if ( ( my.size / o.size ) > 1.2) { // food
                    var f = o.size * localStorage.hunger/ Math.pow(od, 2)
                } else { // enemy
                    var f = - o.size * localStorage.danger / Math.pow(od, 2)
                }
                if (isNaN(f)) return
                aX += (o.x - my.x) * f / od
                aY += (o.y - my.y) * f / od
            })
    
            aX += localStorage.bords / my.x
            aX += -localStorage.bords / Math.abs(my.x - 12000)
            aY += localStorage.bords / my.y
            aY += -localStorage.bords / Math.abs(my.y - 12000)
    
        })
    
        var a = Math.sqrt( Math.pow( aX , 2 ) + Math.pow( aY , 2 ) ) || 1
    
        var offX = aX * 100 / a
        var offY = aY * 100 / a
    
        var mX = Math.abs(canvas.clientWidth / 2 + offX)
        var mY = Math.abs(canvas.clientHeight / 2 + offY)
    
        canvas.onmousemove({ clientX: mX, clientY: mY })
    
    }, 50)
    


    1. WarGot
      11.05.2015 17:55
      +3

      Ну ты сотона


  1. OlegTar
    11.05.2015 16:09
    +2

    Спасибо, за статью, но читерство — это плохо.


  1. Sergey-S-Kovalev
    12.05.2015 08:22
    -1

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

    А смысл тогда играть то? Променять веселье на ботов.


    1. Lexxtor
      12.05.2015 13:07
      +2

      Ботов же можно потом убрать и вместо них придут игроки.
      Можно делать одного бота — это тоже интересно.


    1. Zenitchik
      12.05.2015 16:23
      +4

      Разработка бота сама по себе интереснее любой игры.


  1. Lonsdaleite
    30.05.2015 18:39

    Видимо, что-то изменили. Сейчас не работает. При загрузке — пустое поле.