Все уже наверно в курсе о такой замечательной веб-игре, как 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)
abby
10.05.2015 21:21+1Круто, я тоже пользовался fiddler'ом, но хотел немного более интеллектуального бота. Поделюсь своей наработкой немного деобфусцированного скипта, правда, одной из предыдущих версий, gist.github.com/abby-sergz/65aad7682de388f14ee2.
alhimik45
10.05.2015 21:33Во-первых, сервер раскидывает игроков по игровым комнатам. Поэтому со мной на карту из 50 ботов попадают 2-3. Остальные «играют» в других комнатах, следуя по координатам из соседней Вселенной.
Можно переконнектиться к нужному серверу, зная его IP: в консоли после выбора региона будет написан ip-адрес, потом можно коннектиться к нему:connect("ws://213.219.39.46:443"); //нужный ip сервера
Правда, наверное, нужно будет что-то подправить чтобы это всё работало на одной странице.edolganov Автор
10.05.2015 21:40Если я не ошибся, то сервер на одном ip содержит много игровых комнат. Потому что я подставляю один и тот же ip для всех ботов. Но ко мне попадает лишь часть. Другие крутятся с другими игроками.
В итоге в комнате играет 15-20 игроков, на сервер может и 5000.alhimik45
10.05.2015 21:50Хм, точно. Тестил со вкладками браузера. Если подключаться довольно быстро, то попадаю в одну комнату, если через некоторое время, то уже кидает в другую.
astal
10.05.2015 21:34+2Можно и проще. Перед загрузкой удаляем main_out из страницы и подгружаем свою копию. Пример для GM или аналогов — pastebin.com/7ZhB5cVD
Scorpi
10.05.2015 22:02К сожалению работает не для всех браузеров.
«beforescriptexecute» был убран из Chrome.
MaximChistov
10.05.2015 21:35(del)
akke
11.05.2015 02:04-3В треде на reddit уже всё давно обсудили, и даже разработчик ответил: www.reddit.com/r/Agario/comments/34z3zp
Да и на youtube, к слову, такие же боты-кормильцы 8 дней как выложены: www.youtube.com/watch?v=XyLWCdnff2A
DnAp
10.05.2015 22:43+4Я пошел по другому пути. Ботов не стал делать, а расковырял оригинальную js и сделал пачку улучшений код на githab
vintage
11.05.2015 15:12+13Я пошёл дальше и вообще исключил игрока оставив лишь ботов, которые кооперируются для выживания.
Свой код я инъектирую с помощью отладчика. Достаточно остановить его в любой внутренней функции и мы имеем полный доступ ко внутренним переменным. Так что можно через консоль создать замыкание, вызывающееся по таймеру, и можно творить что угодно.
Инфа о своих клетках находится в хэше «m». Инфа обо всех остальных клетках лежит в словаре «v». Этого уже достаточно для игры. Пробегаемся в цикле по своим клеткам и по чужим и складываем вычисленные для пар «я — не я» ускорения. Потом дёргаем onmousemove, чтобы сообщить движку в какую сторону мы хотим ускориться.
Все чужие клетки делятся на следующие группы:
1. Вирусы — зелёные пассивные клетки, которые могут взрывать остальных. У них стоит флаг isVirus. От них отталкиваемся по обратно квадратичному закону.
2. Друзья — дружеские боты, действующие всоседних вкладках. Их определяем по префиксу в имени. К ним притягиваемся с константной силой. Это позволяет им находить друг друга даже если отреспаунились в разных конца карты.
3. Враги — клетки, которые больше максимального размера, что мы можем съесть. От них отталкиваемся как отвирусов, но с другим коэффициентом.
4. Еда — все остальные клетки, существенно меньше нашей. К ним притягиваемся по обратно квадратичному закону.
Кроме того, чтобы бота не зажимали к стенке, добавляем отталкивание от них в перпендирулярних к ним направляниях по тому же закону обратных квадратов.
Итого это позволяет убегать от группы врагов поедая попадающуюся по пути еду и сливаться с друзьями образуя более крупную особь. Однако люди всё ещё могут хитростью зажать нашего бота в тиски или порвать его простреливая через вирус.
Чтобы отреспауниться, достаточно вызвать функцию setNick(string). Я её вызываю просто всегда при вызове моего замыкания.
Фото рекорда:
Видео с четырьмя ботами:
Код бота без констант - подберите свои значения :-)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)
Sergey-S-Kovalev
12.05.2015 08:22-1Я допускаю, что данное решение может привести в лидерству с куда большей вероятностью, чем это можно сделать самому.
Но самый главный вопрос возникающий при борьбе за бОльшее количество своих ботов в комнате — А смысл сего действа?
В контексте того, что если вы наводните комнату ботами, которые вас накормят до первого места, то места для реальных игроков не останется.
В итоге вы первый среди ботов. Нет конкуренции, нет куража, нет азарта и веселья от ников игроков.
А смысл тогда играть то? Променять веселье на ботов.Lexxtor
12.05.2015 13:07+2Ботов же можно потом убрать и вместо них придут игроки.
Можно делать одного бота — это тоже интересно.
Reeze
Мне единственному кажется, что в этой игре нельзя выиграть благодаря какому-то плану.
Рост в размерах происходит только благодаря великому рандому и он глобально не зависит от твоих действий.
edolganov Автор
Зависит. Можно подкрадываться и съедать конкурентов делением. Можно кидать приманки, можно растить зеленые растения, чтобы они делились и разрывали бОльшего конкурента на части…
Шахматы прям :)
5angel
Да. На самом деле, успех в игре зависит прежде всего от умений игрока, а уже потом от удачи.
Для того, чтобы стабильно выбиваться в лидеры нежно освоить несколько приемов:
1) рассчитывать свою массу и массу противника, чтобы при делении не оказаться в патовой ситуации и не быть съеденным;
2) разбивать лидеров в с помощью зубастых клеток;
3) обмениваться массой между поделившимися клетками;
4) ретироваться делением в случае угрозы со стороны лидеров;
wrewolf
их можно растить?
sferrka
Да, стреляя в них.
edolganov Автор
С помощью клавиши W можно кидать массу в зеленое растение — от этого оно распухает и в какой-то момент делится. Таким образом можно делать ловушки для близких к растению соперников, разбивая их на части прилетевшим к ним новым растением.
wrewolf
вот оно как )