Forum in READ ONLY mode! All questions and discussions on Discord official server, invite link: https://discord.gg/VxsGzJ7

Ивенты (events)

Часто задаваемые вопросы
Post Reply
Fenix
Developer
Developer
Posts: 275
Joined: 24.08.2010 7:20
Location: Ставрополь
Contact:

Ивенты (events)

Post by Fenix »

В связи с изменениями в методе назначения обработчиков (в SetEventProc первый параметр был заменён со строкового на перечисление) пост был изменён.
Выложу по мене разработки несколько примеров по использованию обработчиков событий (ивентов, эвентов, кому как нравится).
Превый пример будет по использованию протокола ICQ:

Code: Select all

program Informer;
const
  ManagerUIN = 0000000000;//Номер учетной записи ICQ для менеджера
  ManagerPass = '********';//Пароль учетной записи ICQ для менеджера
  ParentUIN = 000000000;//Номер учетной записи, для которой разрешено подавать команды скрипту

var
  Terminated: Boolean;
  
procedure OnICQDisconnect;
begin
  //В случае непредвиденного дисконекта попытаемся снова подключиться к серверу
  ICQConnect(IntToStr(ManagerUIN), ManagerPass);
end;

procedure OnICQError(Text: String);
begin
  //При ошибке службы ICQ делаем запись в лог и отключаемся от сервера
  AddToSystemJournal('Ошибка ICQ: ' + Text);
  ICQDisconnect;
end;

procedure OnICQIncomingText(UIN: Cardinal; Text: String);
begin
  //Если команды поступают от доверенной учетной записи, то отрабатываем их.
  if UIN = ParentUIN then begin
    if Text = 'stop' then begin
      AddToSystemJournal('Получена удалённая команда оставновки скрипта');
      Terminated := True;
    end else begin
      ICQSendText(IntToStr(UIN), Text + ': ' + FloatToStrF(GetSkillValue(Text), ffFixed, 3, 1));
    end;
  end;
end;

begin
  //Установим обработчики событий
  SetEventProc(evICQDisconnect, 'OnICQDisconnect');
  SetEventProc(evICQError, 'OnICQError');
  SetEventProc(evICQIncomingText, 'OnICQIncomingText');
  //подключаемся к серверу ICQ
  ICQConnect(IntToStr(ManagerUIN), ManagerPass);
  while not Terminated do begin
    //некоторые выполняемые в цикле действия.
    Wait(2000);
  end;
  //Отключаем обработчики событий
  SetEventProc(evICQDisconnect, '');
  SetEventProc(evICQError, '');
  SetEventProc(evICQIncomingText, '');
  //Отключаемся от сервера ICQ и Ультимы
  ICQDisconnect;
  Disconnect;
end.
Теперь подробное описание принципов работы скрипта.
В начале работы скрипта через SetEventProc устанавливаются обработчики событий отключения от ICQ, ошибки при работе с ICQ и получени сообщения, после чего устанавливается подключение к серверу ICQ (через ICQConnect). Далее скрипт в цикле выполняет свои основные функции (в моём случае очень важный Wait(2000)) более не отвлекаясь на "аську" (не считая деактивации обработчиков событий в конце работы скрипта).

При случайном разрыве соединения с ICQ скрипт автоматически попытается его восстановить. При ошибке по работе с ICQ будет произведена попытка переподключения (на всякий пожарный).
При получении сообщения происходит самое интересное: проверяется UIN отправителя сообщения и если этому UIN разрешено "командовать" стелсом, то обрабатываются полученые в сообщении текстовые команды. На этом всё.

Соответственно учетные данные необходимо будет указать свои. Так же набор возможных команд, передаваемых стелсу через аську тоже может быть доукомплектован. Но это уже на совести разработчика :)

Результат работы данного скрипта в окне ICQ:
Fenix, 28.09.2010 23:38:11:
Magery

Василий Пупкин, 28.09.2010 23:38:11:
Magery: 100,0

Fenix, 23:38:18:
Ncromancy

Василий Пупкин, 23:38:19:
Ncromancy: 0,0

Fenix, 23:38:31:
Necromancy

Василий Пупкин, 23:38:31:
Necromancy: 105,0

Fenix, 23:38:44:
stop
Системный журнал стелса по отработке даннго скрипта:
23:50:07:279 [Mondain]: Compiling
23:50:07:290 [Mondain]: Compiled succesfully
23:50:35:646 [Mondain]: Получена удалённая команда оставновки скрипта
23:50:37:353 [Mondain]: Character Mondain Disconnected.
23:50:37:361 [Mondain]: Succesfully executed
23:50:37:362 [Mondain]: Script ICQ.sc stopped successfuly
После команды "stop" работа скрипта была остановлена и он ушел по-английски (тобишь не попрощавшись). Обращу так же внимание, что в данной реализации "анализатора команд" не предусматривается вольное использование регистра символов в командах. Поэтому команда "Stop" будет проигнорирована и попытается отработаться как запрос значения скила.

P.S.: Следующий пример предполагается посвятить обработчику события получения гампа.
User avatar
Vizit0r
Developer
Developer
Posts: 3958
Joined: 24.03.2005 17:05
Contact:

Post by Vizit0r »

описание evWindowsMessage

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

Если прислать сообщение за номером

Code: Select all

WM_CharMessage = $0445;
на окно стелса, и при этом
в wParam будет ID чара,а в LParam - указатель на массив формата

Code: Select all

TArray = array[0..159999] of Byte;
то этот самый указатель (LParam) летит в (предварительно настроенный!) обработчик ивента evWindowsMessage.
там один параметр, Cardinal.
Поскольку ПС не знает что такое указатели, придется пользоваться костылем-функцией ConvertPointer2TByteArr, которой передается это число-указатель (параметр ивента evWindowsMessage), а возвращает она тот самый изначальный TArray.
Дальше использовать как обычный статический массив.

Code: Select all

Program Test; 
var
gg : TByteArr;
i : Byte;
procedure WindowsMessage(a : Cardinal); 
begin
gg := ConvertPointer2TByteArr(a);
for i := 0 to 10 do 
AddToSystemJournal('gg' + IntToStr(i) + ' = ' + IntToStr(gg[i])); 
end; 
begin 
SetEventProc(evWindowsMessage,'WindowsMessage'); 
while True do wait(1000); 
end.
слать (НЕ из паскальскрипта) примерно так

Code: Select all

var
 myrec : TArray;
begin
myrec[0] := 1;
myrec[1] := 7;
myrec[3] := 7;
myrec[5] := 3;
SendMessage(FindWindow('TStealthForm',nil),WM_CharMessage,Char_ID,dword(@myrec));
...
для ПС есть 2 функции:

Code: Select all

1) function SendMessageToWindow(hWnd : Cardinal;CharID : Cardinal;value : TByteArr): Integer;
2) function GetSelfHandle: Cardinal;
пример использования (шлем своему же окну):

Code: Select all

Program Test;
var
gg : TByteArr;
begin 
gg[1] := 8;        
SendMessageToWindow(GetSelfHandle,Self,gg);
end.
Для других стелсовых окон - придется сначала ID чара и окна передать через глобалку, или через чат, или еще как - и тогда использовать


Где это можно использовать:

1) как коллбек от окон, нарисованных из стелса - например в кач-ве реакции на нажатие кнопок, и т.д. (правильное безглючное рисование окон - это отдельная тема, раскрою позже).
2) как удобное общение другие программ со стелсом. теперь уже скрипту не надо в цикле опрашивать чужие проги, а достаточно просто обработчик прицепить, и слать в стелс сообщения при необходимости.
3) межстелсовое взаимодействие.
"Пишите код так, как будто сопровождать его будет склонный к насилию психопат, который знает, где вы живете". (с) Макконнелл, "Совершенный код".
Post Reply