Что может быть интереснее процесса игры в игры? Правильно! Процесс наблюдения за тем, как играет в игры написанный тобой бот.
Некоторое время, я размышлял, о чём бы написать свою первую статейку. Хотел написать о программировании микроконтроллеров, но оказалось трудно отделить части рабочих проектов от тех, что можно опубликовать без оглядки на коллег :-) Остановился на идее о ботах.
Введение
Боты для онлайн игр я бы грубо разделил на 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. Говоря ИИ, я подразумеваю программу способную получать и обрабатывать (анализировать) информацию, планировать и выполнять действия в соответствии с целями и результатами анализа ситуации.
Автор: ru_master