Быстрая эмуляция нажатия клавиш в боте для браузерки - C#
Формулировка задачи:
Пишу бота для браузерных танчиков.Прицеливание работает по принципу:через BitBlt копируется нужное окно в память, там ищутся группы пикселей определенного цвета(цвет имени врага), и если таковые найдены, нужно жать определенную кнопку(z или x) до тех пор, пока разница по абсциссе между центром имени противника и центром своих жизней(по ним определяется направление пушки), не станет меньше определенного числа(25 пикселей в данном случае). Эмуляцию нажатия клавиш реализую через PostMessage, ибо работать должно в неактивном окне. Все вроде пашет, но загвоздка в том, что пушка нередко "проезжает" цель, и начинается вертеться перед ней туда-сюда, то бишь PostMessage об отпускании клавиши не доходит вовремя. Сам алгоритм определения, когда надо остановиться, в ряд ли может быть неверным, ибо 100 раз проверен и в целом работает(то есть останавливается вовремя в тех случаях, когда пушка вертится медленнее обычного, либо просто удачно повернулась). Сообщения, которые я посылаю, полностью соответствуют аналогичным при реальном нажатии клавиш(через Spy++ проверялось). Кроме того при попытке эмулировать через keybd_event получилось ровно то же самое. Чего только не пробовал-и задержки разные, и сообщения, все то же самое. Скачал виртуальную клавиатуру-при попытке управлять с нее все работает нормально, то есть теоретически такая эмуляция возможна. Сразу скажу, что производительность при обработке изображения тут не причем, FPS достаточный, чтобы несколько кадров попало в положение, когда надо остановиться. На данный момент прога работает с гугл хромом, там проще однозначно достать хэндл нужного окна. Часть кода прилагается, желающим могу скинуть заготовку проги в личку. Жду ваших предположений, как это устранить, я пока что в тупике( но выход ищу. Заранее благодарен. Рабочие способы реализовать такое на других языках и по другим принципам тоже буду рассматривать, ибо делаю чисто ради развлечения и саморазвития, никуда не спешу. Эти куски кода миллион раз переписывались, на мелкие несуразности можете не обращать внимание, главное-указанный выше вопрос.
[DllImport("USER32.DLL", EntryPoint = "PostMessageW", SetLastError = true,
CharSet = CharSet.Unicode, ExactSpelling = true,
CallingConvention = CallingConvention.StdCall)]
public static extern bool PostMessage(int hwnd, int Msg, int wParam, int lParam);
public static bool e5 = false;
public static bool e6 = false;
public static bool e4 = false;
public static bool in5 = true;
public static bool in6 = true;
public static bool in4 = true;
public static System.Timers.Timer t4 = new System.Timers.Timer(60);
public static System.Timers.Timer t5 = new System.Timers.Timer(60);
public static System.Timers.Timer t6 = new System.Timers.Timer(60);
public static void t4_Tick(object source, ElapsedEventArgs e)
{
try
{
if (e4)
{
if (!in4)
{
int virtual_code = MapVirtualKey(0x11, 1);
int lp = 0x11 << 16 | 1;
PostMessage((int)win3, WM_KEYDOWN, virtual_code, lp);
in4 = true;
}
else
{
int virtual_code = MapVirtualKey(0x11, 1);
int lp = 0x11 << 16 | 1 << 30 | 1;
PostMessage((int)win3, WM_KEYDOWN, virtual_code, lp);
}
}
else
{
if (in4)
{
int virtual_code = MapVirtualKey(0x11, 1);
int lp = 1 << 31 | 1 << 30 | 0x11 << 16 | 1;
PostMessage((int)win3, WM_KEYUP, virtual_code, lp);
in4 = false;
}
}
}
catch { }
}
public static void t5_Tick(object source, ElapsedEventArgs e)
{ try
{
if (e5)
{
if (!in5)
{
int virtual_code = MapVirtualKey(0x2D, 1);
int lp = 0x2D << 16 | 1;
PostMessage((int)win3, WM_KEYDOWN, virtual_code, lp);
in5 = true;
}
else
{
int virtual_code = MapVirtualKey(0x2D, 1);
int lp = 0x2D << 16 | 1 << 30 | 1;
PostMessage((int)win3, WM_KEYDOWN, virtual_code, lp);
}
}
else
{
if (in5)
{
int virtual_code = MapVirtualKey(0x2D, 1);
int lp = 1 << 31 | 1 << 30 | 0x2D << 16 | 1;
PostMessage((int)win3, WM_KEYUP, virtual_code, lp);
in5 = false;
}
}
}
catch { }
}
public static void t6_Tick(object source, ElapsedEventArgs e)
{ try
{
if (e6)
{
if (!in6)
{
int virtual_code = MapVirtualKey(0x2C, 1);
int lp = 0x2C << 16 | 1;
PostMessage((int)win3, WM_KEYDOWN, virtual_code, lp);
in6 = true;
}
else
{
int virtual_code = MapVirtualKey(0x2C, 1);
int lp = 0x2C << 16 | 1 << 30 | 1;
PostMessage((int)win3, WM_KEYDOWN, virtual_code, lp);
}
}
else
{
if (in6)
{
int virtual_code = MapVirtualKey(0x2C, 1);
int lp = 1 << 31 | 1 << 30 | 0x2C << 16 | 1;
PostMessage((int)win3, WM_KEYUP, virtual_code, lp);
in6 = false;
}
}
}
catch { }
}
private void threader() {
t4.Elapsed += new ElapsedEventHandler(t4_Tick);
t5.Elapsed += new ElapsedEventHandler(t5_Tick);
t6.Elapsed += new ElapsedEventHandler(t6_Tick);
t4.Interval = 100;
t5.Interval = 100;
t6.Interval = 100;
t4.Enabled = true;
t5.Enabled = true;
t6.Enabled = true;
while (true)
{
try
{
mozgi();
}
catch { }
}
}
private void mozgi()
{
//тут делается снимок окна и находится minraznica.X = модуль разницы абсциссы между
//центром имени врага и центром своей пушки. Корректно, проверено. А так же
//переменная pravo, определяющая сторону, с которой находится враг.
//enemynamesectorREAL это массив точек цвета имени врага.
if (enemynamesectorREAL.Count > 0 && e4)
{
e4 = false;
}
if (enemynamesectorREAL.Count > 0 && minraznica.X > 25 && (!(e5 || e6)))
{
if (pravo)
{
e5 = true;
}
else
{
e6 = true;
}
}
if ((pravo && e5) || (!pravo && e6) || enemynamesectorREAL.Count == 0 || minraznica.X <= 25)
{
bull = false;
}
else { bull = true; }
if ((minraznica.X <= 25 || bull) && (e5 || e6) && enemynamesectorREAL.Count != 0)
{
if (e5)
{
e5 = false;
int virtual_code = MapVirtualKey(0x2D, 1);
int lp = 1 << 31 | 0 << 30 | 0x2D << 16 | 1;
PostMessage((int)win3, WM_KEYUP, virtual_code, lp);
{
notbuisy = false;
}
}
if (e6)
{
e6 = false;
int virtual_code = MapVirtualKey(0x2C, 1);
int lp = 1 << 31 | 1 << 30 | 0x2C << 16 | 1;
PostMessage((int)win3, WM_KEYUP, virtual_code, lp);
{
notbuisy = false;
}
}
}
}
else { }
if ((enemynamesectorREAL.Count == 0 && !notbuisy) || (mls == 0 && !notbuisy))
{
if (e5 || e6)
{
notbuisy = true;
}
else
{
Random rand = new Random();
switch (rand.Next(1))
{
case 0:
e5 = true;
notbuisy = true;
break;
case 1:
e6 = true;
notbuisy = true;
break;
}
}
}
if ((enemynamesectorREAL.Count == 0 && !e4) || (mls == 0 && !e4))
{
e4 = true;
}
//тут можно вывести обрабатываемый битмап на экран с пометками, что удобно при отладке.
mem.Dispose();
}Решение задачи: «Быстрая эмуляция нажатия клавиш в боте для браузерки»
textual
Листинг программы
BitmapData bmpData = mem.LockBits(new Rectangle(0, 0, mem.Width, mem.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
byte[] bitmapBuffer = new byte[bmpData.Stride * bmpData.Height];
Marshal.Copy(bmpData.Scan0, bitmapBuffer, 0, bitmapBuffer.Length);
for (int j = 0; j < (int)ypos; j++)
{
int strideY = bmpData.Stride * j;
for (int i = 0; i < (int)xpos; i++)
{
int index = strideY + i * byteLen;
RGBcolor x = new RGBcolor();
x.B = bitmapBuffer[index + 0];
x.G = bitmapBuffer[index + 1];
x.R = bitmapBuffer[index + 2];
if (x.R == mylife.R && x.G == mylife.G && x.B == mylife.B)
{
Coords coord;
coord.X = i;
coord.Y = j;
if (lifeminx > i) { lifeminx = i; }
if (lifemaxx < i) { lifemaxx = i; }
if (lifeminy > j) { lifeminy = j; }
if (lifemaxy < j) { lifemaxy = j; }
mylifesector.Add(coord);
mls++;
}
if ((Math.Abs(x.R - enemyname.R) < 20) && (Math.Abs(x.G - enemyname.G) < 30) && (Math.Abs(x.B - enemyname.B) < 20))
{
Coords coord;
coord.X = i;
coord.Y = j;
enemynamesector.Add(coord);
ens++;
}
if ((Math.Abs(x.R - colorpuli.R) < 20) && (Math.Abs(x.G - colorpuli.G) < 25) && (Math.Abs(x.B - colorpuli.B) < 15))
{
Coords coord;
coord.X = i;
coord.Y = j;
colorpulisector.Add(coord);
cps++;
}
}
}
mem.UnlockBits(bmpData);