Pull to refresh

Бот для Flash-игры / Внедряемся в Flash

Game development *

В рамках недели ботов для браузерных игр.

Эта неделя богата на статьи о ботах для бразуерных игр.
Во всех статьях для управления ботом использовался AutoIT. Это простой и хороший способ чтобы начать делать бота «в лоб», он не требует никаких знаний об игре, кроме как знания правил и графического интерфейса. Для серверной части игры такой бот вообще будет выглядеть как обычный пользователь, если не брать во внимание невероятную усидчивость и производительность такого игрока.

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

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

Выбор жертвы


Первым делом я опробовал этот способ на игре Diamond Dash в Google+, которая сразу же покорно согласилась выполнять все мои желания. Для статьи на Хабре я попытался выбрать какую-нибудь другую из представленных игр в Google+, но часть из них требует понимания правил, еще немного игр на JavaScript, а оставшиеся простые на Flash оказались более устойчивы и требуют значительной предварительной подготовки. О внедрении в последние я расскажу в отдельной статье, когда подготовлю необходимые инструменты.

Поэтому препарировать я буду снова Diamond Dash. За одно сравним полученные результаты с результатами из первой статьи.

Идея


Очевидным решением проблемы с распознаванием мира является внедрение во внутренности игры с целью получения точных данных о состоянии мира. В голову приходят два способа:
  1. Декомпилировать, внедрить свой код и собрать обратно
  2. Каким-то другим способом получить доступ к объектам

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

Поэтому мы пойдем вторым путём. Это избавит нас изучения всего кода и позволит сосредоточиться только на самом главном.

Prerequisites


Для выполнения нашей задачи потребуются:

Исследования


Начнём исследование игры с её подключения к De Monster Debugger. Это очень интересный инструмент для отладки Flash. Даже если вы не собираетесь ставить рекорды в браузерных играх рекомендую его поставить хотя бы ради того, чтобы поиграть в небольшой квест, демонстрирующий возможности этого отладчика.

После установки Монстра и прохождения квеста напишем небольшой кусок кода:

MonsterConnector.as
Полученный SWF файл будет служить обёрткой для всех загружаемых swf и подключать их к отладчику, после того как мы пропишем путь к нему в %USERPROFILE%\mm.cfg:

PreloadSWF=c:\temp\MonsterConnector.swf

Перезапускаем браузер и идём играть в Diamond Dash. Как только игра загрузится наш прелоадер подключит её к Monster Debugger:


Можно немного поковыряться в игре через сам дебаггер, чтобы немного понять её устройство. В дальнейшем это избавит от анализа всего исходного кода. В нашем случае проинспектировав игровое поле можно достаточно быстро узнать что класс кубика называется Brick (неожиданно, правда?).

Теперь нам всё же понадобится декомпилировать код, но не с целью его модификации, а лишь затем чтобы немного изучить.
Для этих целей отлично подойдёт Sothink SWF Decompiler, либо можно воспользоваться бесплатным ASDec (он пока в стадии ранней беты, но зато позволяет править байткод, что в исследованиях кому-то тоже может пригодиться).

Первым делом найдём что происходит при клике мыши. Поискав по тексту MouseEvent натыкаемся на уже известный нам класс Brick, который реагирует на клик мышью:



И генерирует событие EVENT_BRICK_CRASH у GameManager.instance со своими координатами:



Как видим, у класса GameManager есть публичное статичное свойство instance. Это нам на руку — достаточно найти этот класс и можно начинать генерировать поддельные клики.
Находим класс:

gameManager = loader.applicationDomain.getDefinition('pl.fabrykagier.collapse::GameManager');

Бегло просмотрев код класса GameManager находим событие EVENT_START_GAME. Подписавшись на него мы будем знать когда можно начинать кликать.

А теперь поиграем


Монстр нам больше не нужен, дальше мы будем работать самостоятельно. Можно удалить код Монстра и добавить проверку на адрес загружаемой swf, чтобы не пытаться играть в баннеры на Хабре :)

if (loader.loaderURL != 'https://secure.f**tprint.net/w**ga/g**gle_test/Diamond.swf') return;

А теперь попробуем генерировать клики по всем кубикам подряд:


MonsterConnector.as

Перезапускаем браузер, запускаем игру…



Работает!
Но хороших результатов нам таким образом не добиться, а кристаллы, которые появляются при безошибочных кликах, мы так вообще никогда не увидим.

Теперь пора определить состояние поля и действовать более осмысленно.

Немного исследовав код мы найдём что все кубики хранятся в свойстве grid объекта типа GameArea, но беда в том, что это свойство приватно и получить к нему доступ напрямую мы не можем. Сам класс GameArea не предоставляет нам никаких публичных методов для получения положения кубиков. В нём же есть функция findBiggestGroup для определения самой большой группы, которая нам могла бы пригодиться, но она как на зло тоже оказалась приватной.
Но в начале статьи мы уже видели класс Brick в Монстре. Значит, мы можем просто найти все эти кубики в сцене.

Я сделал это просто рекурсивно перебрав все видимые объекты в сцене. Возможно, есть способ проще.



Ну а теперь копируем найденную ранее функцию findBiggestGroup, адаптировав её под наше представление данных. Любители алгоритмов и ненавистники рекурсивных функций могут написать свою :)

Очередной запуск…



Всё поле было безошибочно «разобрано», за что нам надавали кучу кристаллов, которые мы забыли собрать. Не смотря на это мы получили неплохой результат, переплюнув бота на AutoIT!



Кристаллы это те же самые экземпляры Brick, но со значением color = 21. Будем кликать по ним сразу же при обнаружении.



Запускаем еще раз, ждём минуту, и…



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

Финальная версия MonsterConnector.as

Итоги


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

В следующий раз я постараюсь рассказать что можно сделать если разработчик был более предусмотрителен и не оставил публичных свойств и функций, либо просто строго придерживается модели MVC, лишив нас возможности найти данные в сцене.
Tags:
Hubs:
Total votes 79: ↑77 and ↓2 +75
Views 57K
Comments Comments 27