Игровые боты. Начало

от автора

Что может быть интереснее процесса игры в игры? Правильно! Процесс наблюдения за тем, как играет в игры написанный тобой бот.

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

Введение

Боты для онлайн игр я бы грубо разделил на 3 разновидности по способам реализации:
1. Боты не использующие приложение игры. Имитирующие протокол обмена с сервером.
2. Боты работающие с процессом приложения игры. В случае с Web, работающие с окном браузера.
3. Боты работающие со скриншотом и имитирующие устройства ввода мышь и клавиатуру.

Первая разновидность скорее гипотетическая, т.к. протоколы, как правило, закрыты и не тривиальны.

Вторая разновидность более реальна и может быть реализована. Бот второго вида получает полезную информацию из памяти процесса игры. Недостаток — версии клиентов могут регулярно обновляться и тогда может потребоваться заново искать интересующие адреса памяти.

Мы рассмотрим третюю разновидность ботов, т.к. ИМХО они более привлекательны, хоть и не лишены недостатков.

В этой статье я рассмотрю набор инструментов для самого простого бота для Windows.

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

Код

Для разработки приложений я буду использовать Qt Creator + Qt 5 либы (так мне привычнее) и раз бот для Виндовс то + windows.h (WinAPI).

Инклуды:

#include <windows.h>   // WinAPI #include <iostream>    // std::cout #include <unistd.h>    // sleep(), usleep() #include <math.h> 

Хидер бота:

// Индексы точек enum {     menu=0,     elm_1,     points_cnt };  class MyBot { public:     MyBot();     void run();     void move_to(int inx);     void lclick_to(int inx);     void rclick_to(int inx);     void drag(int from_inx,int to_inx);     POINT point[points_cnt]; }; 

Конструктор:

MyBot::MyBot() :     // Массив координат органов управления (по которым мы будем кликать мышкой)     point({       {100,100}, // 0 - menu       {130,130}, // 1 - elm_1     }) { } 

Регистрация горячих кнопок для управления ботом:

    RegisterHotKey((HWND)Widget::winId(), 101, MOD_ALT, VK_F1);    // Запуск бота     RegisterHotKey((HWND)Widget::winId(), 102, MOD_ALT, VK_F2);    // inx++     RegisterHotKey((HWND)Widget::winId(), 103, MOD_ALT, VK_F3);    // Проверить точку inx     RegisterHotKey((HWND)Widget::winId(), 104, MOD_ALT, VK_F4);    // Запомнить точку inx     RegisterHotKey((HWND)Widget::winId(), 105, MOD_ALT, VK_F5);    // Вывести в консоль массив координат 

Обработка событий нажатия кнопок управления ботом:

int inx=0; MyBot bot;  bool Widget::nativeEvent(const QByteArray & eventType, void * message, long * result){     Q_UNUSED(result);     Q_UNUSED(eventType);     MSG* msg = reinterpret_cast<MSG*>(message);     if(msg->message!=WM_HOTKEY)return false;      switch(msg->wParam){     case 101: // Alt-F1 - запуск бота         bot.run();         return true;     case 102: // Alt-F2 - inx++         if(inx<points_cnt-1)inx++;         return true;     case 103: // Alt-F3 - Проверить точку inx         bot.move_to(inx);         return true;     case 104: // Alt-F4 - Запомнить точку inx         GetCursorPos(&point[inx]);         return true;     case 105: // Alt-F5 - Вывести в консоль массив координат         for(i=0;i<points_cnt;i++){             std::cout << "{" << bot.point[i].x << "," << bot.point[i].y << "}, //" << i << std::endl;         }         return true;     }     return false; } 

Перемещение указателя мыши к нужной точке:
(выполнено не очень аккуратно, обещаю исправиться :-))

#define width 1920 #define height 1080  void MyBot::move_to(int inx){     int x=point[id].x;     int y=point[id].y;     POINT pt;     GetCursorPos(&pt);      int from_x=pt.x;     int from_y=pt.y;      int to_x=x;     int to_y=y;      int dx=to_x-from_x;     int dy=to_y-from_y;      float fdx;     float fdy;     int loop_cnt;      if(abs(dx)>abs(dy) && dx!=0){         fdx=dx<0? -1.0 :1.0;         fdy=(float)dy/abs(dx);         loop_cnt=abs(dx);     }     else if(dy!=0){         fdy=dy<0? -1.0 :1.0;         fdx=(float)dx/abs(dy);         loop_cnt=abs(dy);     }     else return;      // двинуть за 1 секунду     int time=1000000/loop_cnt;     float fx=from_x;     float fy=from_y;     for(int i=0;i<loop_cnt;i++){         fy+=fdy;         fx+=fdx;         int nx=(fx)*(65536 / width);         int ny=(fy)*(65536 / height);         mouse_event(MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE,nx,ny,0,0);         usleep(time);     }     usleep(50000); } 

Клики:

void MyBot::lclick_to(int inx){     move_to(inx);     mouse_event(MOUSEEVENTF_LEFTDOWN,0,0,0,0);     usleep(50000);     mouse_event(MOUSEEVENTF_LEFTUP,0,0,0,0);     usleep(100000); }  void MyBot::rclick_to(int inx){     move_to(inx);     mouse_event(MOUSEEVENTF_RIGHTDOWN,0,0,0,0);     usleep(50000);     mouse_event(MOUSEEVENTF_RIGHTUP,0,0,0,0);     usleep(100000); } 

Перетаскивание:

void MyBot::drag(int from_inx, to_inx){     move_to(from_inx);     usleep(50000);     mouse_event(MOUSEEVENTF_LEFTDOWN,0,0,0,0);     usleep(70000);     move_to(to_inx);     mouse_event(MOUSEEVENTF_LEFTUP,0,0,0,0);     usleep(30000); } 

Работа бота:

void MyBot::run(){     rclick_to(menu);  // кликнем правой кнопкой для вызова контекстного меню     lclick_to(elm_1); // кликнем левой кнопкой по строке меню } 
Мануал юзера

Перед запуском бота горячей кнопкой Alt-F1, бот следует сначала настроить, определив верные координаты органов управления по которым бот будет кликать.
Для запоминания координат точки наводим указатель месту и жмём Alt-F4.
Для проверки корректности точки отводим указатель в сторону и жмём Alt-F3.
Для настройки следующей точки жмём Alt-F2.
Для сохранения верных координат жмём Alt-F5.

Подводные камни

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

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

Qt Creator: qt-project.org/downloads
Исходный код проекта на гитхабе: github.com/rumaster/my_bot_v1

P.S. Не подумайте что я ярый противник онлайн игр, раз публикую исходники ботов. Я противник дискриминации ИИ (ботов) и за развитие онного. А ещё, игры — двигатель прогресса.

P.P.S. Говоря ИИ, я подразумеваю программу способную получать и обрабатывать (анализировать) информацию, планировать и выполнять действия в соответствии с целями и результатами анализа ситуации.

ссылка на оригинал статьи http://habrahabr.ru/post/212779/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *