Написание плагина для оболочки клиента TimeZero

Требования:

I. Требования к плагину:

  1. В плагине обязательно должно быть тем или иным образом указано имя (ник) лица, его написавшего.

  2. Интерфейс плагина должен быть выполнен так, чтобы с ним было удобно работать, элементы управления не должны "наползать" друг на друга.

  3. Если плагин предназначен для каких-либо расчётов - обязательно обрабатывайте возможные ошибки и информируйте пользователя о них.

  4. Информирующие диалоговые окна обязательно должны содержать имя плагина. Его следует указывать в заголовке или в теле сообщения.

  5. Плагин обязательно должен представлять из себя один файл, если это необходимо - все изображения/звуки/видео, требуемые для работы плагина, должны быть в виде ресурсов, присоединённых к нему. Ограничения на создание файлов в процессе работы нет (распаковка ресурсов, сохранение настроек и пр.).

  6. Плагин не должен изменять каких-либо данных в файлах настроек/локализации клиента. Разрешается только чтение.

  7. Плагин не должен требовать установку или наличие какого-либо дополнительного софта для своей работоспособности.

II. Плагин нужно отправить на электронный адрес full_bugs@timezero.ru, где указать:

  1. В теме письма "Плагин" и название плагина (пример: "Плагин - Киборг").

  2. Ник и/или реальное имя человека его написавшего.

  3. Полное описание плагина, все возможности и особенности (это необходимо для модерации).

III. В приложении(attachment) к письму:

  1. Должен быть ZIP-архив с исходниками плагина с названием: "Название плагина (source).zip".

  2. Должен быть ZIP-архив с откомпилированным плагином.

  3. Отдельно (вне архивов) должен быть прикреплён текстовый файл с кратким описанием плагина и его возможностями (для публикации на сайте).

IV. Исходник плагина должен быть хорошо откомментирован и оформлен. В письме должно указываться используете ли вы какие-либо дополнительные компоненты и, если они требуют регистрации в системе - подробно описать это. Обязательно укажите версию среды разработки.


Необходимые технические аспекты:

Плагин для оболочки игрового клиента TimeZero является библиотекой (dll) с расширением tzp. Чтобы изменить директиву расширения вашего плагина — укажите {$E tzp} после строки с названием вашей библиотеки. (library MyPlugInName;)



Инициализация плагина в оболочке клиента:
— В момент загрузки плагина оболочка клиента вызывает функцию PluginVerifying, в ней плагин должен вернуть своё имя и версию, формата Major.Minor.Release.Build (без точек). В имени плагина допускаются русские символы.
function PluginVerifying: TPlugInfo;
begin
  // Возвращаем оболочке игрового клиента имя плагина
  Result.PluginName    := 'MyPlugInName';
  // Возвращаем оболочке игрового клиента версию плагина
  Result.PluginVersion := 1000;
end;

Данная функция должна быть добавлена в раздел exports где перечисляются функции, доступные для вызова из оболочки клиента.
Запись информации о плагине выглядит следующим образом:
  TPlugInfo = record
    // Имя плагина. (Допускаются русские символы)
    PluginName    : WideString;
    // Версия плагина. Major.Minor.Release.Build (без точек)
    PluginVersion : Word;
  end;

— Если функция успешно выполнилась оболочка клиента вызывает процедуру PluginInitialization и передаёт ей в параметрах ссылку на класс, через который плагин будет общаться с GUI и игровым клиентом.
procedure PluginInitialization(PlugClass:TPlugInInterface);
begin
  // Свяжем указатель с глобальной переменной плагина
  PlugInterface := PlugClass;
end;

PluginInitialization также необходимо добавить в раздел exports.
Переменная PlugInterface — класс "интерфейса" общения оболочки игрового клиента с плагином (см. ниже).

Завершение работы плагина:
— В момент закрытия оболочки клиента или удаления плагина вызывается процедура PluginFinalization (без параметров). Если вам необходимо совершить какие-то действия в момент закрытия плагина (сохранение настроек; освобождение объектов) вы можете сделать их в теле этой процедуры. В таком случае, не забудьте добавить её в раздел exports.


Листинг описания класса "интерфейса" общения оболочки игрового клиента с плагином:

  TPlugInInterface = class
  private
    FShortCut    : TShortCut;
    FIcon        : TMemoryStream;
    FCaption     : WideString;
    FOnCall      : TNotifyEvent;
    FOnGameEvent : TNotifyEvent;
  public
    // Горячая клавиша для быстрого обращения к плагину
    property ShortCut: TShortCut read FShortCut write FShortCut;
    // Иконка вашего плагина в формате PNG
    property Icon: TMemoryStream read Ficon write FIcon;
    // Заголовок плагина для ячейки меню и горячих клавиш оболочки клиента
    property Caption: WideString read FCaption write FCaption;

    // Событие обращения пользователя к плагину
    property OnCall: TNotifyEvent read FOnCall write FOnCall;
    // Оповещение плагина о возникновении игрового события
    property OnGameEvent: TNotifyEvent read FOnGameEvent write FOnGameEvent;

    // Процедура изменения настроек плагина (Icon, ShortCut, Caption, Events) в оболочке клиента
    procedure SetPlugInParams; virtual; stdcall; abstract;
    // Функция передачи данных в игру
    function  SetGameData(Request, Value: OleVariant; Player: WideString): Boolean; virtual; 
	stdcall; abstract;
    // Функция получения данных из игры
    function  GetGameData(Value: WideString; Player: WideString): OleVariant; virtual; stdcall; 
	abstract;
    // Функция выполнения команды в оболочке игрового клиента
    function  TZExecute(Command: WideString; Value: WideString; Player: WideString): OleVariant; 
	virtual; stdcall; abstract;
    // Функция получения списка игроков загруженных в игровые закладки. gValue — служебный параметр
    function  GetPlayersList(const gValue: Byte = 0): TStringList; virtual; stdcall; abstract;
  end;


Параметры и события плагина:

Изменять параметры плагина вы можете в любой момент работы оболочки клиента.
Пример изменения параметров:
procedure PluginInitialization(PlugClass:TPlugInInterface);
begin
  // Свяжем указатель с глобальной переменной плагина
  PlugInterface := PlugClass;
  // Изменяем параметры плагина
  PlugInterface.ShortCut := Menus.TextToShortCut('Ctrl+P');
  PlugInterface.Caption  := 'Мой постядерный плагин';
  // Сообщим оболочке клиента об изменениях
  PlugInterface.SetPlugInParams;
end;

Чтобы ваш плагин мог отвечать на обращения пользователя к нему — необходимо создать обработчик событий OnCall типа TNotifyEvent, для вашего PlugInterface. Тогда при нажатии указанного в ShortCut сочетания горячих клавиш или нажатии кнопки в панели инструментов/меню выбора плагина оболочки клиента — будет срабатывать ваше событие.
Если плагин не указывает свой обработчик события OnCall — оболочка клиента не будет его отображать в меню выбора плагинов.

Событие OnGameEvent предназначено для перехвата некоторых игровых событий плагинами.
По умолчанию плагины могут видеть следующие события:
— Персонаж вошёл в игру
— Персонаж вышел из игры

Создав обработчик события OnGameEvent вам будет необходимо распознавать его тип и значение. Для этого вам потребуется добавить небольшой класс, отвечающий за его описание.
— Класс, описание игрового события:
  TGameEvent = class
  private
    FEventName  : WideString;
    FEventValue : WideString;
    FEventCode  : Integer;
  public
    // Имя персонажа, в игровой закладке которого произошло событие
    property EventName: WideString read FEventName;
    // Значение события
    property EventValue: WideString read FEventValue;
    // Код события
    property EventCode: Integer read FEventCode;
  end;

Коды событий, доступных плагинам по умолчанию:
Вход в игру (EventCode: 0)
Выход из игры (EventCode: 1)

Пример обработчика события OnGameEvent:
procedure TMyPlugInForm.OnGameEvent(Sender: TObject);
begin
  case TGameEvent(Sender).EventCode of
    0 : Dialogs.MessageDlg('Персонаж "'+TGameEvent(Sender).EventValue+'" вошёл в игру.',
	mtInformation,[mbOK],0);
    1 : Dialogs.MessageDlg('Персонаж "'+TGameEvent(Sender).EventValue+'" вышел из игры.',
	mtInformation,[mbOK],0);
  end;
end;


Как применить PNG иконку к плагину:

Необходимые требования к иконке:
  • Высота/Ширина: 24 на 24
  • Глубина цвета: 32 BPP
  • Максимальный размер файла 5 Kb.
После того, как вы определитесь с выбором PNG картинки для вашего плагина — вам следует создать ресурс-файл, который будет "вшит" в плагин.
Для этого:
  • Создайте текстовый документ блокнота.
  • Напишите в нём следующий текст: MYICON PNG "MyPlugInIcon.png"
  • Где MyPlugInIcon.png — имя вашей PNG картинки.
  • Сохраните файл, рядом с вашей PNG картинкой, под именем pngicon.rc
  • Создайте текстовый документ блокнота.
  • Напишите в нём следующий текст: Brcc32 pngicon.rc
  • Сохраните файл, рядом с вашей PNG картинкой, под именем pngicon.bat
  • Запустите файл pngicon.bat (Двойным кликом мыши), в итоге вы получите файл pngicon.RES, который появится в том же каталоге.
  • Скопируйте файл pngicon.RES в корневой каталог вашего плагина.
Ресурс-файл с PNG иконкой создан. Теперь, следует его подключить в вашем плагине.
Для этого добавьте директиву {$R pngicon.res} после главного раздела uses.

Пример загрузки иконки плагина из ресурсов:
procedure PluginInitialization(PlugClass:TPlugInInterface);
var
  ResourceStream : Classes.TResourceStream;
begin
  // Свяжем указатель с глобальной переменной плагина
  PlugInterface := PlugClass;
  // Изменяем параметры плагина
  PlugInterface.ShortCut := Menus.TextToShortCut('Ctrl+P');
  PlugInterface.Caption  := 'Мой постядерный плагин';
  // Загружаем PNG иконку плагина
  if Windows.FindResource(HInstance, PChar('MYICON'), 'PNG')<>0 then
     begin
       PlugInterface.Icon := TMemoryStream.Create;
       ResourceStream     := TResourceStream.Create(HInstance, 'MYICON', 'PNG');
       PlugInterface.Icon.LoadFromStream(ResourceStream);
       ResourceStream.Free;
     end;
  // Сообщим оболочке клиента об изменениях
  PlugInterface.SetPlugInParams;
end;


Общение плагина с игровой flash:

Общаться с игровой flash ваш плагин может следующими способами:
— Через функции SetGameData/GetGameData
— Используя функцию TZExecute указать оболочке клиента, особое игровое событие, за которым он (плагин) хочет следить. К примеру, плагин сообщит, что желает следить за перемещением персонажа по локациям, а ответ он будет получать через событие OnGameEvent.
Для второго способа — вам необходимо связаться с разработчиком оболочки игрового клиента, чтобы обсудить нюансы и получить разрешение на его использование. Почта для связи: full_bugs@timezero.ru, в теме письма необходимо указать "Плагин" и название плагина (пример: "Плагин — Киборг").

Описание функции передачи данных в игру:
function SetGameData(Request, Value: OleVariant; Player: WideString): Boolean;

Request — имя запроса к игровой flash.
Value — значение запроса.
Player — имя персонажа, в игровой закладке которого будет выполнен этот запрос (см. ниже).
Функция возвращает успех передачи данных типа Boolean.

Пример использования:
if not PlugInterface.SetGameData('_root.watchUserInfo.login','Keeper Ranson','') then
   Dialogs.MessageDlg('Не удалось открыть информацию о персонаже Keeper Ranson',mtWarning,[mbOK],0);

В примере используется запрос _root.watchUserInfo.login, который отвечает за отображение информации о персонаже в игровой flash. В качестве ника персонажа указано Keeper Ranson, а параметр Player пустой - это означает, что плагин не указывает оболочке клиента определённую игровую закладку, в которой следует выполнить эту функцию, она будет выполнена в текущей закладке, в случае если её тип игровой и в ней авторизирован игрок.

Описание функции получения данных из игры:
function GetGameData(Value: WideString; Player: WideString): OleVariant;

Value — данные, которые плагин хочет получить от игровой flash.
Player — имя персонажа, в игровой закладке которого будет выполнен этот запрос (см. ниже).
Функция возвращает ответ от игровой flash, в случае если запрос указан корректно. Возвращаемый тип — OleVariant.

Пример использования:
var
  GameLogin : OleVariant;
begin  
  GameLogin := PlugInterface.GetGameData('_root.USER.login','');
  if (TVarData(GameLogin).VType and VarTypeMask)<>varEmpty then
     Dialogs.MessageDlg('Это игровая закладка с персонажем "'+String(GameLogin)+'".',
	 mtInformation,[mbOK],0)
  else Dialogs.MessageDlg('Это не игровая закладка, либо в ней не авторизирован персонаж.',
  mtWarning,[mbOK],0);

В примере используется запрос _root.USER.login, который отвечает за получение данных о нике персонажа, параметр Player снова пустой - плагин совершает запрос в текущей закладке.

Указав в запросе _root.USER.login вместо login любой из ниже перечисленных параметров вы можете получить более детальную информацию о персонаже:
login		- Имя персонажа
HP 		- Текущее количество здоровья
maxHP 		- Максимальное количество здоровья
str 		- Сила
dex 		- Ловкость
int 		- Интуиция
pow 		- Выносливость
acc 		- Меткость
intel 		- Интеллект
exp 		- Опыт (обратите внимание, что это значение текущего опыта персонажа)
nextlevel 	- Необходимое количество опыта для достижения следующего уровня
level 		- Уровень
pro 		- Профессия. Идентификатор имеет следующие значения:
  		1=Профессия: корсар
  		2=Профессия: сталкер
  		3=Профессия: старатель
  		4=Профессия: инженер
  		5=Профессия: наемник
  		6=Профессия: торговец
  		7=Полицейский: патрульный
  		8=Полицейский: штурмовик
  		9=Полицейский: специалист
  		10=Профессия: журналист
  		11=Профессия: чиновник
  		12=Профессия: псионик
  		13=Каторжник
  		14=Профессия: пси-кинетик
  		15=Профессия: пси-медиум
  		16=Профессия: пси-лидер
  		17=Профессия: полиморф
		30=Профессия: дилер
propwr 		- Уровень профессии (значение от 0 до 1)
man 		- Пол персонажа
virus 		- Наличие заражения вирусом X
psy 		- Текущий уровень пси-энергии
maxPsy 		- Максимальный уровень пси-энергии

Местонахождение:
X 		- Первая координата местонахождения персонажа
Y 		- Вторая координата местонахождения персонажа 
(Координаты даются в формате 0..360, а не -180..180) ROOM - Номер комнаты в шахте Навыки: sk0 - Холодное оружие sk1 - Легкое оружие sk2 - Среднее оружие sk3 - Тяжелое оружие sk4 - Метательное sk5 - Энергетическое sk6 - Медицина sk7 - Минирование sk8 - Электроника sk9 - Пси-контроль sk10 - Пси-атака sk11 - Пси-медицина sk12 - Пси-искажение


Взаимодействие плагина с оболочкой игрового клиента:

Если вы хотите сообщить оболочке клиента для какого персонажа выполняется тот или иной запрос — вам следует указать ей параметр Player. В нём указывается никнейм игрока авторизированого в игровой закладки оболочки клиента.
Получить никнейм вы можете организовав обработчик события OnGameEvent, который позволит вам перехватывать моменты входа и выхода персонажей в игру, или воспользоваться следующей функцией:
function GetPlayersList(const gValue: Byte = 0): TStringList;

Параметр gValue — является служебным, он вам не пригодится.
Пример использования функции:
var
  PlayersList : TStringList;
  PlayersStr  : String;
  i : integer;
begin
  PlayersList := PlugInterface.GetPlayersList;
  if (Assigned(PlayersList)) and (PlayersList.Count<>0) then
     begin
       for i:=0 to PlayersList.Count-1 do PlayersStr := PlayersStr+PWideChar(PlayersList.Strings[i])+#13#10;
       Dialogs.MessageDlg('Текущий список персонажей: '+#13#10+PlayersStr,mtInformation,[mbOK],0);        
     end else Dialogs.MessageDlg('Игровых закладок с авторизированным персонажем - нет.',mtWarning,[mbOK],0);
  SysUtils.FreeAndNil(PlayersList);
end;


Описание функции выполнения команды в оболочке игрового клиента:
function TZExecute(Command: WideString; Value: WideString; Player: WideString): OleVariant;

Command — имя команды.
Value — значение команды.
Player — имя персонажа, в игровой закладке которого будет выполнена эта команда.
Функция возвращает результат обработки команды, в случае если запрос указан корректно. Возвращаемый тип — OleVariant, определяется в зависимости от типа запроса.

Пример использования:
if not PlugInterface.TZExecute('BrowserNavigate','//www.timezero.ru','') then
   Dialogs.MessageDlg('Не удалось открыть страницу //www.timezero.ru',mtWarning,[mbOK],0);


Список команд функции TZExecute, доступных плагинам по умолчанию:
GetShellHandle - Получение главного дескриптора оболочки клиента (Integer).
BrowserNavigate - Открытие ссылки во встроенном браузере оболочки клиента. Value — URL.
CheckIgnoring - Проверка наличия персонажа Value в списке игнорируемых у игрока Player.
ClearGameChat - Очистка игрового чата у персонажа Player.
SendToChat - Отправка сообщения в игровой чат персонажа Player.

Если у вас есть какие-нибудь идеи или вам необходимо получать дополнительные данные от оболочки игрового клиента — обратитесь к разработчику по почте: full_bugs@timezero.ru, в теме письма необходимо указать "Плагин" и название плагина (пример: "Плагин — Киборг").


Организация общения между идентичными плагинами по сети:

Оболочка игрового клиента позволяет плагинам организовать транспортный протокол для передачи данных между двумя, или более, модулями. PlugTalk, похож на UDP протокол. Не требует предварительной установки соединения с желаемым пользователем, быстро и эффективно доставляет данные, прост в использовании.
Следует учитывать, что аналогично UDP/IP протокол PlugTalk не гарантирует доставку пакетов — следовательно разработчику плагина необходимо предусмотреть возможность ожидания ответа и реализовать схему типа «запрос-ответ».
PlugTalk работает в строковом режиме, максимальная длина данных PlugTalk-сообщения равна ~150 символов. Организовать общение между модулями можно только в том случае, если они идентичны друг другу, т.е. их MD5 сумма исполняемой библиотеки не отличается.

Доступ к функционалу происходит через метод TZExecute класса "интерфейса" общения оболочки игрового клиента с плагином.
Команды для управления протоколом передачи данных плагином:
StartPlugTalk - Активировать протокол в закладке персонажа Player. Возвращает успех операции (Boolean).
StopPlugTalk - Остановить работу протокола в закладке персонажа Player. Возвращает успех операции (Boolean).
SendPlugData - Отправить данные Value от игрового персонажа персонажа Player (см. ниже). Возвращает успех операции (Boolean).

Команда SendPlugData позволяет плагину отправить, указанному в параметре Value, персонажу данные в случае если модуль активировал протокол PlugTalk.
Для успешной доставки PlugTalk-сообщения, параметр Value обязательно должен содержать имя персонажа, для которого предназначены эти данные. Рекомендуется указывать ник персонажа с первого символа в формате SysUtils.Format('[%s]',[ToPlayer]); где переменная ToPlayer является строковым идентификатором пользователя (ник), к которому уйдут последующие данные в этой строке.

Пример использования команды:
if not PlugInterface.TZExecute('SendPlugData',SysUtils.Format('[%s]%s',['Keeper Ranson','hello']),'') then
   Dialogs.MessageDlg('Не удалось отправить данные ”hello” персонажу Keeper Ranson',mtWarning,[mbOK],0);

Для получения входящих данных — модулю необходимо реализовать обработчик события OnGameEvent, активировать PlugTalk протокол и ожидать возникновения игрового события с EventCode равным 9.

Пример обработчика события OnGameEvent для получения входящих данных от протокола PlugTalk:
procedure TMyPlugInForm.OnGameEvent(Sender: TObject);
var
  Data,Author: String;
  p          : Integer;
begin
  case TGameEvent(Sender).EventCode of
    0 : Dialogs.MessageDlg('Персонаж "'+TGameEvent(Sender).EventValue+'" вошёл в игру.',mtInformation,[mbOK],0);
    1 : Dialogs.MessageDlg('Персонаж "'+TGameEvent(Sender).EventValue+'" вышел из игры.',mtInformation,[mbOK],0);
    9 : begin
          Data := TGameEvent(Sender).EventValue;
          // Парсим автора входящих данных
          p := Pos('[',Data); if p=0 then exit; Delete(Data,1,p);
          p := Pos(']',Data); if p=0 then exit;
          Author := Copy(Data,1,p-1); Delete(Data,1,p);
          Dialogs.MessageDlg('От персонажа "'+Author+'" пришли данные: '+Data,mtInformation,[mbOK],0);
        end;
  end;
end;

Исходя из примера, видно, что в EventValue, по аналогии с отправкой данных, первыми символами содержится ник персонажа (автор входящих данных).

Рекомендуется не совершать автоматическую отправку данных, а реализовывать её с согласия пользователя, т.е. игрок должен самолично «нажать кнопку», чтобы данные «ушли». Также, следует упомянуть, что если плагин будет слишком часто отправлять данные по протоколу PlugTalk — то к игроку, от чьего имени они уходили, будут применены соответствующие санкции.

Если у вас есть какие-нибудь вопросы, требующие более детальной конкретизации — обратитесь к разработчику по почте: full_bugs@timezero.ru, в теме письма необходимо указать "Плагин" и название плагина (пример: "Плагин — Киборг").


Тестирование работоспособности плагина:

Плагины, которые не прошли проверку администрацией игры — помечаются статусом "не поддерживается". Для возможности тестирования вашего плагина вам необходимо запускать оболочку игрового клиента с параметрами:
"-debugPlugIn -PlugInName.tzp".



За плагины прошедшие проверку и размещенные в разделе управления модулями оболочки игрового клиента, выплачивается вознаграждение, в зависимости от качества и объема работы.

Архив с примером плагина (14,4 КБ)