Как написать бота для игры в браузере
Есть онлайн игра во Флэш Плеере, играют в нее через окно браузера. Игра простая - в зависимости от картинок нужно нажимать на соответствующие кнопки, без всяких сложных движений мышкой и вообще без клавиатуры. Мне нужно написать для нее бота на Python (знаю только его). Раньше я такого никогда не делал, поэтому мне виднеется один вариант: сделать скриншоты всех необходимых картинок из игры в соответствующих координатах, для этого использовать pyscreenshot; написать код для собственно игры; с помощью autopy двигать мышкой и кликать в необходимые места.
Вопрос: это нормальный подход? Возможный?
Или пробовать другие варианты, пытаться найти API этой игры или каким еще образом пишут подобных ботов?
Игра онлайн, там участвуют другие игроки, какие либо вмешательства сайт заметит 100%, а вариант со скриншотами можно запалить (в смысле использования бота) только по "нечеловеческим" движениям мышки. Но это можно легко обойти, дописав рандомные движения.
Итак, рабочий ли это подход? Может у кого есть информация\гайд про такое?
2 ответа 2
С ботами для флеш игр надо малость покопаться. Насчёт Phyton ничего не могу сказать - это специфичный язык. Но можно написать бота написать бота на ActionScript .
Внутри флеш-приложения, все анимационные элементы и кнопки организованы в дерево элементов. Оно весьма похоже на DOM дерево в web-странице: по признакам можно легко найти любой элемент в дереве, например кнопку; можно узнать какого цвета кнопка, видима ли она; можно легко узнать её координаты. Доступ ко флеш приложению из стророннего приложения можно получить через PreloadSwf.
Суть такова - вы устанавливаете флеш debug версии , в папку пользователя windows добавляете конфиг mm.cfg . В конфиг прописываете параметр:
И перед загрузкой абсолютно любой флешки в браузере, например: загрузится PreloadSwf.swf . И он будет иметь полный доступ к приложению: сможет перебирать дерево элементов, сможет эмулировать щелчки или нажатия клавиш.
Соответственно PreloadSwf вы пишете сами, внутри перед загрузкой проверяете по формальным параметрам вроде названия: та ли это игра. Если да - запускаете бота.
На том же принципе preloadswf основан популярный плагин для отладки flashfirebug
Если вы как и я любите онлайн игры, но не любите тратить на них сильно много времени, добро пожаловать под кат. Мы не будем обсуждать боты это хорошо или нет, а просто разберем как можно для конкретной онлайн игры сделать бота. Он будет не тупо клацать по кнопке по таймауту, а будет реагировать на события в браузере. Это мы сделаем с помощью Аспектно-ориентированного программирования (далее AOP). Для примера я выбрал полюбившуюся хабром игру Пернатск.
1. Готовим ингредиенты
- Собствена сама игра. Я буду показывать на примере Пернатска
- Браузер. У меня все стандартно — Chrome
- Текстовый редактор или в чем вы будете редактировать JS код. Notepad++ подойдет
- Аккаунт для тестов, который не жалко будет потерять в результате бана
Важно! Игра должна работать в браузере, а не в клиенте. Причем не на Flash, а на HTML+JavaScript.
На выходе у нас должно получиться расширение для Chrome, которое будет играть вместо нас.
2. Делаем расширение
О том как делается расширение я не буду подробно расписывать. На хабре об этом уже писали, например, тут.
Приведу лишь коды, нужных нам файлов.
В manifest.json
Важно! Если вы не понимаете, что мы делаем в этой единственной функции, то делать бота вам пока рано. Почитайте основы JavaScript.
Вся работа у нас будет вестись в файле injected.js Его код пока такой:
Все эти файлы сохраняем в одной папке bot.
3. Первый пуск бота
Заходим в Chomre в Настройки — Инструменты — Расширения, жмем на «Загрузить распакованное расширение». У вас должна быть стоять галочка на «Режим разработчика». Указываем папку с нашими файлами.
Теперь зайдем в нашу игру. Включим консоль разработчика (нам это придется делать часто) — жмем F12 и увидим «Hello Wolrd». Приложение заработало.
Теперь все, что мы пропишем в функции ai_on будет обрабатываться после загрузки страницы.
4. Добавляем AOP
Для работы бота нам потребуются библиотеки. Мой любимый jQuery уже используется на Пернатске, поэтому добавлять его не будет.
Добавим плагин AOP for Jquery. По хорошему это стоило запаковать в само расширение в виде отдельного файла, но я ленив. Поэтому просто добавим код bin/aop.pack.js первой строкой в наш injected.js.
Проверим, что это работает изменив ai_on
5. Зачем мы будем использовать AOP
Суть AOP можно очень примерно выразить следующим образом: "После функции_1, надо сделать функция_2. Что это за функция_1 нас крайне мало волнует. Она отработала, значит надо должна отработь наша. Лезть в функцию_1 мы не будем." Еще раз повторюсь это очень примерно. Лучше прочитайте нормальное описание например тут
Как это использовать? Мы после любой функции можем запустить нашу. Например, в браузере отработала функция о том, что в зоне видимости появился монстр. Запускаем функцию напасть на него, которую мы уже написали сами.
6. Учим бота первой команде
В injected.js добавим такой код:
По этой команде наша бот-птичка будет лететь в Пернатске за шишками. Код слегка мудренный, так как в Пернатске есть небольшая защита от ботов.
Когда вы будете писать свои команды я рекомендую сначала опробовать их работоспособность в console, а уже потом переносить код в редактор.
Чтобы протестировать и проверить работу нашей команды запустим в косноле код commands.conessearch() Все работает.
7. Ищем событие на которое должен реагировать бот
Тут есть два метода первый — анализируем код игры. Долго :(
Второй метод — воспользоваться AOP, и после всех функций, который срабатывали вывести в лог их имя. Потом выбрать нужные.
Меняем ai_on()
У нас пойдет много-много функция. Там будет $ от jQuery или стандартная setTimeout.
Работать с этим очень не удобно изменим код еще раз.
Теперь у нас отражаются только те функции, которые еще не отображались. Их полный список мы храним в fnList.
После пары минут там будут такие варианты функции для прицепки ["clearInterval", "$", "setTimeout", "timerTick", "serverTimeUpdate", "getComputedStyle", "setInterval", "tutorialArr", "showQ", "showQc", "updateBirdData", "viz", "unviz", "weatherUpdate"]
Меняя target и регулярное выражение в method мы можем подобрать ту функцию, которая нам подойдет, чтобы к ней прицепиться. Для примера, я выбираю функцию weatherUpdate теперь каждый раз как будет меняться погода наша птичка будет лететь за шишками.
Сегодня я получил ссылочку на статью на хабре о технологии создания «макроса-бота для браузерной игры». Там же было написано с сожалением, что AutoIT мало представлен на хабре. Со штуками, описанными в статье я баловался год-два назад. В последнее время использую библиотеку IE.au3, которая позволяет творить с браузерными игрушками просто чудеса.Собственно информацией об этом и хотел бы поделиться. Только сразу предупреждаю — речь идет только работе под MS-Internet Explorer. Фанатам других браузеров скажу сразу — можно тоже самое делать наверное под любым браузером, только нужно искать соответствующую библиотеку и как они работают я сказать не могу. Скажу только о библиотеке IE.au3 — она входит в комплект стандартной установки AutoIT-а, достаточно хорошо протестирована, снабжена комментариями и примерами, описанные в ней функции удобно подсвечиваются и предлагаются к завершению при наборе с соответствующими подсказками, как стандартные функции пакета (или как там назвать этот самый AutoIT).
Выбор жертвы
Вариант №1 — примитивный вариант
Ищем на экране куда тыкать, и тыкаем «мышей» куда надо. Сразу скажу – это вариант неправильный, но для общей эрудиции я приведу текст скрипта с пояснениями.Кусок №1: ищем на экране «точку отсчета», за которую можно будет зацепиться и от неё считать местоположение квадратиков куда будем обтыкивать поле.
Func GetTopLeftCorner ( $window )
Global $x = 0 , $y = 0
WinActivate ( $window , "" )
If WinActive ( $window ) Then
$size = WinGetPos ( "[active]" )
For $i = 1 To $size [ 3 ]
If CheckRGB ( PixelGetColor ( $x + $i , $y + $i ) , Dec ( "402215" ) , Dec ( "5A352A" ) ) Then
If CheckRGB ( PixelGetColor ( $x + $i - 259 , $y + $i ) , Dec ( "331A0D" ) , Dec ( "624232" ) ) Then
For $j = $i To 1 Step - 1
If not CheckRGB ( PixelGetColor ( $i + 1 , $j ) , Dec ( "000000" ) , Dec ( "3A301D" ) ) Then
$x = $i
$y = $j
ExitLoop ( 2 )
EndIf
Next
ExitLoop
EndIf
EndIf
Next
EndIf
EndFunc
Функция проверяет цвета точек, пробегая по диагонали, ищет левый край окна игры в браузере. Затем просматривает цвета точек, пробегая вверх, ищет верхнюю границу окна игры. Найденную точку я буду считать «точкой отсчета». Расстояние от неё до каждой клетки на поле игры фиксированное и легко вычисляется по индексу.Результат работы функции возвращается в глобальных переменных $x и $y, которые соответствуют координатам точки по горизонтали и вертикали соответственно.При вызове функции в качестве параметра нужно указать заголовок окна программы. Его можно задать вручную, определив предварительно с помощью утилиты AutoIt Window Info, которая устанавливается вместе с AutoIt-ом.Приведенный выше скрипт использует самодельную функцию CheckRGB:
Func CheckRGB ( $color , $min , $max )
Local $rgb [ 3 ] [ 3 ]
$res = True
$rgb [ 2 ] [ 0 ] = BitAND ( $min , Dec ( "FF0000" ) ) / Dec ( "10000" )
$rgb [ 1 ] [ 0 ] = BitAND ( $min , Dec ( "00FF00" ) ) / Dec ( "100" )
$rgb [ 0 ] [ 0 ] = BitAND ( $min , Dec ( "0000FF" ) )
$rgb [ 2 ] [ 1 ] = BitAND ( $color , Dec ( "FF0000" ) ) / Dec ( "10000" )
$rgb [ 1 ] [ 1 ] = BitAND ( $color , Dec ( "00FF00" ) ) / Dec ( "100" )
$rgb [ 0 ] [ 1 ] = BitAND ( $color , Dec ( "0000FF" ) )
$rgb [ 2 ] [ 2 ] = BitAND ( $max , Dec ( "FF0000" ) ) / Dec ( "10000" )
$rgb [ 1 ] [ 2 ] = BitAND ( $max , Dec ( "00FF00" ) ) / Dec ( "100" )
$rgb [ 0 ] [ 2 ] = BitAND ( $max , Dec ( "0000FF" ) )
For $i = 0 To 2
If $rgb [ $i ] [ 0 ] > $rgb [ $i ] [ 1 ] Or $rgb [ $i ] [ 1 ] > $rgb [ $i ] [ 2 ] Then
$res = False
ExitLoop
EndIf
Next
Return $res
EndFunc
— Функция проверяет цвет точки, передаваемой в качестве первого параметра $color на то, чтобы она находилась в цветовом диапазоне от $min до $max, с учетом трех составляющих цвета точки (RGB). Возвращает, соответственно True или False.
Непосредственно процедура обтыкивания всех клеток на поле выглядит так:
For $i = 1 To 12
For $j = 1 To 10
MouseClick ( "left" , $x + ( $i * 40 ) + 103 , $y + ( $j * 40 ) + 172 , 1 , 1 )
Sleep ( 25 )
Next
Next
Тут все понятно. 10 и 12 в заголовках цикла это размер поля в клетках. Константы в параметрах MouseClick вычислены с помощью той же утилиты AutoIt Window Info, относительно определенной ранее «точки отсчета». Значение задержки в Sleep можно менять по своему желанию – это будет влиять на скорость обтыкивания, но слишком быстрое может приводить к ошибкам выполнения скрипта игры.
Преимущество этого метода – он работает в любом браузере. Везде где можно открыть окно игры так, чтобы была видна вся пашня.Недостаток – окно игры должно быть всегда открыто, нужно подбирать скорость обтыкивания и во время работы скрипта нельзя трогать мышь (иначе тыкнет не там где надо).
Вариант №2 — правильный вариант
Мы не используем указатель мыши вообще. Вместо этого, мы тыкаем на нужном объекте, загруженном в браузере IE по его ID. (ID этих клеток можно посмотреть в HTML коде загруженной страницы, они имеют названия f1-f120 соотвественно).
Local $i , $j , $n
Local $oIE = _IEAttach ( $gamewindowname )
For $i = 1 To 12
For $j = 0 To 9
$obj = _IEGetObjById ( $oIE , "f" & ( 12 * $j + $i ) )
If $obj <> 0 Then $obj . click ( )
Next
Next
Вариант №3 — самый правильный вариант
Недостатком предыдущего варианта является то, что когда нам нужно обтыкать поле, нам нужно переключиться из игры и запустить скрипт. Это неудобно. По-настоящему удобный скрипт не должен заставлять выполнять дополнительные действия в целях экономии на других рутинных операциях. Поэтому, я просто заменяю стандартное действие в игре «посадить одну семачку» на «засадить все поле выбранными семенами». Выглядит этот так:
$obj = _IEGetObjById ( $oIE , "anpflanzen" )
If $obj <> 0 Then _
$obj . outerHTML = "… тут кусок HTML кода, который для удобства привожу отдельно “
Сам код и скрипты на JavaScript позаимствованы из оригинального HTML кода игры и подгружаемого вместе с ней файла скриптов, и немного переделаны с полива на посадку.
Не так давно на Google+ появились игры. Прочитав топик об этом, я решил во что нибудь поиграть. Выбор пал на игру Diamond Dash. Через некоторое время игры программист во мне заговорил, что однотипные действия нужно автоматизировать. И вот что из этого вышло…
*Примечание: «руками» даже опытному игроку сложно набрать больше 400к
Раньше я никогда не сталкивался с задачами работы с экраном и мышкой. После непродолжительного гугления было решено для решения использовать язык макросов AutoIt.
Под катом вы найдете краткое описание игры, мой способ распознавания поля, алгоритм определения точки нажатия, и некоторое количество оптимизаций. А так же ссылку на github-репозиторий скрипта.
UPD Добавлено видео работы скрипта.
Краткое описание игры
Игра представляет из себя простую «кликни-на-область-больше-трех-квадратиков-одного-цвета» головоломку.
Есть поле 9 на 10, заполненное квадратиками 5 цветов. У нас есть одна минута на то, чтобы набрать максимальное количество очков. При нажатии на область из 3 или более одноцветных клеток, она исчезает, то что над ней проваливается, а сверху падает недостающее. Количество начисленных очков зависит от размера области: чем она больше — тем больше очков.
Кроме того, если делать клики быстро, и почти безошибочно, поле вокруг загорается, а каждое удаление(в данном случае взрыв), захватывает соседние с удаляемой областью клетки.
И наконец последняя особенность: наверху есть шкала с алмазом в конце, которая заполняется по мере уничтожения клеток(и уменьшается при ошибочных кликах). Когда она заполняется, на поле в случайном месте появляется горящий алмаз. При клике на него время останавливается, а сверху поля падает огненный шар, уничтожая все клетки в строке и столбце, где находился камень.
Определение координат окна
Эта часть была добавлена в самую последнюю очередь, до этого координаты угла были жестко прописаны в коде. Используется функция из сторонней библиотеки ImageSearch для поиска сохраненного шаблона 10 на 10 пикселей. Судя по всему, фон слегка меняется от игры к игре, потому что не любой кусок подходил.
На форумах повсеместно не рекомендуют использование ImageSearch из-за долгого времени работы. Но так как нам нужно определить координаты только один раз в начале игры, провисаний по времени можно не опасаться.
Распознавание цветов и сохранение скриншотов
Для определения цвета пикселя по его координатам в AutoIt есть функция PixelGetColor. Но как показала практика, измерение всего 90 пикселей занимает полторы секунды, что, конечно, не очень хорошо. Но справедливости ради надо сказать, что бот написанный с использованием этой функции набирал 400-600 тысяч очков, а это больше чем может набрать человек, даже при большой сноровке.
На форумах был найден метод сохранения текущего состояния монитора в Bitmap, используя WinAPI. Кстати, этот битмап, при необходимости(например для дебага), можно сохранить в файл функцией _ScreenCapture. Далее смотрим цвета 90 пикселей, расположенных по решетке и по ним строим таблицу цветов квадратиков.
- Func _GetField ( ByRef $aiField ) ; получение массива цветов поля
- ; получение BitMap-снимка экрана с помощью WinAPI
- Local $hWnd = WinGetHandle ( "Игры Google+ - Google Chrome" )
- Local $Size = WinGetClientSize ( $hWnd )
- Local $hDC = _WinAPI_GetDC ( $hWnd )
- Local $hMemDC = _WinAPI_CreateCompatibleDC ( $hDC )
- Local $hBitmap = _WinAPI_CreateCompatibleBitmap ( $hDC , $Size [ 0 ] , $Size [ 1 ] )
- Local $hSv = _WinAPI_SelectObject ( $hMemDC , $hBitmap )
- _WinAPI_BitBlt ( $hMemDC , 0 , 0 , $Size [ 0 ] , $Size [ 1 ] , $hDC , 0 , 0 , $SRCCOPY )
- _WinAPI_SelectObject ( $hMemDC , $hSv )
- _WinAPI_DeleteDC ( $hMemDC )
- _WinAPI_ReleaseDC ( $hWnd , $hDC )
- Local $L = $Size [ 0 ] * $Size [ 1 ]
- Local $tBits = DllStructCreate ( 'dword[' & $L & ']' )
- _WinAPI_GetBitmapBits ( $hBitmap , 4 * $L , DllStructGetPtr ( $tBits ) )
- ; определение цветов клеток
- For $iCol = 0 To $iNumCols - 1
- For $iRow = $iNumRows - 1 to 0 Step - 1
- ; замер цвета квадратика
- $iX = $iCornerX + ( $iCol * 40 ) + $iDeltaX
- $iY = $iCornerY + ( $iRow * 40 ) + $iDeltaY
- $iPixelColor = Mod ( DllStructGetData ( $tBits , 1 , $iY * $Size [ 0 ] + $iX ) , 0x1000000 )
- $aiField [ $iRow ] [ $iCol ] = _GetCheckColor ( $iPixelColor )
- Next
- Next
- ; удаление данных для избежаня утечки памяти
- _WinAPI_DeleteObject ( $hBitmap )
- _WinAPI_DeleteObject ( $hMemDC )
- _WinAPI_DeleteObject ( $tBits )
- EndFunc
Тут стоит оговориться, почему замер происходит всего по 1 точке. Этот метод был испробован мной в первую очередь, и остался в финальной версии. Между этими двумя моментами было испробовано довольно большое количество альтернативных способов, среди которых были: замер 64 точек на каждый квадратик(решетка 8 на 8) и различные усреднения полученных значений, случайный выбор координат для замера, хранение истории нескольких последних замеров для лучшей точности… Но все они оказались менее точными или удобными, чем самый первый способ.
Возможно, что так как я весьма далек от темы распознавания изображений, я не знаю чего-то простого, способного помочь мне в этом вопросе. В таком случае буду рад любым предложениям. =)
Определение одноцветной области по таблице цветов
Ну, и, наконец, осталось найти подходящее место и кликнуть туда. Для этого обходим поле снизу вверх (потому что все падает вниз, а значит снизу менее пусто чем сверху), и проверяем одноцветные части. Я делал это с помощью алгоритма безрекурсивного поиска в глубину (DFS). Вкратце суть такова: кладем в стек стартовую клетку, а дальше, пока стек не пуст, достаем из него текущую клетку и обходим ее соседей, при совпадении цвета кладем в стек. Ну да что говорить, код понятней. =)
- Func _DfsAreaSize ( ByRef $aiField , $iStartX , $iStartY ) ; нерекурсивный алгоритм поиска размера одноцветной области
- ; методом поиска в глубину
- Local $aiResult [ $iNumCols * $iNumRows ] [ 2 ] ; список клеток входящих в область
- Local $iResultSize = 0
- Local $afMap [ $iNumRows ] [ $iNumCols ] ; флаги пройденности
- For $iRow = 0 to $iNumRows - 1
- For $iCol = 0 to $iNumCols - 1
- $afMap [ $iRow ] [ $iCol ] = False
- Next
- Next
- $afMap [ $iStartX ] [ $iStartY ] = True
- Local $aiStack [ $iNumRows * $iNumCols ] [ 2 ] ; активный стек
- Local $iStackSize = 1
- $aiStack [ 0 ] [ 0 ] = $iStartX
- $aiStack [ 0 ] [ 1 ] = $iStartY
- While $iStackSize > 0
- $iStackSize -= 1
- $iX = $aiStack [ $iStackSize ] [ 0 ]
- $iY = $aiStack [ $iStackSize ] [ 1 ]
- $aiResult [ $iResultSize ] [ 0 ] = $iX
- $aiResult [ $iResultSize ] [ 1 ] = $iY
- $iResultSize += 1
- For $iDirection = 0 to 3 ; перебор 4 рядомстоящих клеток
- Local $iNewX = $iX
- Local $iNewY = $iY
- Switch $iDirection
- Case 0
- $iNewY += 1
- Case 1
- $iNewY -= 1
- Case 2
- $iNewX += 1
- Case 3
- $iNewX -= 1
- EndSwitch
- If ( $iNewX >= 0 And $iNewX < $iNumRows And _
- $iNewY >= 0 And $iNewY < $iNumCols And _
- Not ( $afMap [ $iNewX ] [ $iNewY ] ) And $aiField [ $iNewX ] [ $iNewY ] = $aiField [ $iStartX ] [ $iStartY ] ) Then
- $afMap [ $iNewX ] [ $iNewY ] = True
- $aiStack [ $iStackSize ] [ 0 ] = $iNewX
- $aiStack [ $iStackSize ] [ 1 ] = $iNewY
- $iStackSize += 1
- EndIf
- Next
- WEnd
- Return $iResultSize
- EndFunc
Оптимизации
Оптимизация 1. Алмазики
Написанного выше уже хватало для получения миллиона. Но мне хотелось больше. Самым большим недостатком вышеописанного алгоритма было, пожалуй, неумение определять алмазы. Поэтому под конец минуты поле выглядело примерно так:
А между тем, алмазы — очень полезная вещь, потому что пока падает огненный шар, таймер останавливается, а квадратики падают. А значит пробелы заполняются, и ошибок будет меньше.
Для определения алмазов для начала пришлось поиграть с координатами замеров, чтобы цветные клетки определялись корректно, а алмазы — не попадали ни под один из их цветов. После этого создаем массив $aiDiams размером 3 (будем проверять только нижние 3 строки, потому что все алмазы рано или поздно туда упадут) * ширину (в нашем случае — 10). При каждом замере просматриваем нижние 3 строки, и если ячейка определилась, то обнуляем соответствующее место в $aiDiams, иначе инкрементируем его. Таким образом для клеток с алмазиками значения будут велики. При накоплении некоего порога, кликаем.
- For $iRow = $iNumRows - 1 to $iNumRows - 3 Step - 1
- For $iCol = 0 to $iNumCols - 1
- If $aiField [ $iRow ] [ $iCol ] <> 0 Then
- $aiDiams [ $iRow ] [ $iCol ] = 0
- Else
- $aiDiams [ $iRow ] [ $iCol ] += 1
- If $aiDiams [ $iRow ] [ $iCol ] > 15 Then
- MouseClick ( "Left" , $iCornerX + 30 + ( $iCol * 40 ) , $iCornerY + 10 + ( $iRow * 40 ) , 1 , $iMouseSpeed )
- $aiDiams [ $iRow ] [ $iCol ] = 0
- Sleep ( 500 )
- Return 0
- EndIf
- EndIf
- Next
- Next
Оптимизация 2. Over Explosion
Оптимизация 3. Область последнего клика
Защита от повторной ошибки, в эффективности которой я не уверен. Дело в том, что в самой игре при ошибке клетка становится серого цвета, а серый цвет алгоритмом определяется как неопределенный (тавтология получилась =) ). Но хуже эта проверка точно сделать не может, поэтому пусть живет.
Суть в том, что при каждом клике сохраняем область, по которой кликаем, и при следующем клике не трогаем ее.
Вместо постскриптума. Пара слов об AutoIt
Предположим есть браузеры в виде клиентов и есть сервер. И общаются они между собой с помощью WebSocket.
Как правило, простой вариант координации в таком варианте, выглядит в виде пачки ифов и с стороны клиента, и с стороны сервера. Пример клиента:
А активность бота всегда должен продуцировать сервер и транслировать всем остальным. И вот на этом как раз основная дилема. Пока что приходит в голову идея что нужно прописать в каждую активность игроков то есть на каждый if (e.e == 'move') или на каждый if (e.e == 'shot') какое то действие бота.
Это наверно может выглядеть как то так:
Но дальше на этом фантазия заканчивается. Вопрос в том как лучше организовать перемещения бота и в архитектуре игры?
Возможно уже есть на других языках похожие библиотеки на Python или на PHP любом другом.
А может не перегружать код сервера еще и действиями бота. Может сделать бота отдельно в виде клиента, который коннектится к серверу почти как обычный пользователь.
В принцепе тоже вариант, но я не совсем представляю как это реализовать. Потому что клиенты вроде как с браузера конектятся 1 браузер 1 клиент. А бот откуда возьмется?
Ну нам же нужен совершенно нестандартный клиент, потому как ему не надо делать то, что делают остальные - т.е. отображать в браузере произошедшее в игре. Т.е. его можно написать на любом языке. Желательно что бы для этого языка была клиентская библиотека для WebSocket, хотя как я понимаю протокол там не сложный, реализовать не долго
Ну и все же я не совсем понимаю как его реализовать в таком случае? Это должен быть еще один процесс запущенный на сервере и эмитирующий браузер? Или это должен делать браузер первого подключившегося клиента?
да, процесс на сервере. эмуляцией браузера это можно назвать с большой натяжкой. браузер обычно делает много того, что боту совершенно не нужно. Да, он должен отвечать серверу по тому же протоколу, по которому работает обычный клиент. Ну и посылать запросы для выполнения своих действий как игрока
2 ответа 2
Лучше было бы, если бы ядро вашего приложения/игры не зависело так от вида транспорта
Схема получается такой
- Игрок совершил действие
- По websocket-ам данные передали на сервер
- Сервер передал данные движку/ядру игре
- Игра обработала данные, сделала необходимые обновления
- Игра возвращает новое состояние себя серверу
- Сервер отправляет всем обновленное состояние
Отсюда получается, что сервер - это лишь посредник между игрой и клиентом.
В примере это выглядит так:
Как это поможет с ботом?
При выполнении команды ботом, сообщайте серверу, что нужно разослать данные, можно добавть к объекту ws фу-нкции или кастомное событие, которое будет за это отвечать. Придется правда переписать логику игры, ведь теперь игроки будут храниться в объекте Game , зато у вас не будет проблем в дальнейшем, так как вы отделили транспортный модуль от игрового.
Читайте также: