Все взаимодействия между плагинами происходят с помощью сообщений. Любое сообщение имеет тег и данные (данные могут быть равны null, если они не нужны).
На каждое сообщение могут быть подписаны ноль или несколько плагинов. Каждый подписанный на тег плагин получает все сообщения, которые отправлены с этим тегом.
Если требуется выбор из нескольких альтернативных реализаций (то есть сообщение должен получить один плагин, а не все сразу), то рекомендуется использовать механизм "альтернатив" (см. core:register-alternative).
Рекомендуется ограничиться следующими типами данных для сообщения: String, Integer, Float, Double, Map<String, Object>, List. При этом последние два типа должны содержать только объекты вышеперечисленных типов.
В редких случаях возможно применение других типов.
Это необходимо для того, чтобы любое сообщение могло быть сериализовано и десериализовано в JSON.
Для экспериментов возможно использовать вкладку Debug (Отладка) диалога настроек DeskChan. В верхнем поле ввода можно указать тег сообщения, а в нижнем - данные в формате JSON. При нажатии кнопки сообщение будет отправлено от имени плагина gui.
id плагина
Посылается ядром каждый раз, когда загружается новый плагин.
Это не обычное сообщение. При подписке на него подписчику сразу же будут присланы сообщения на каждый уже загруженный плагин. Таким образом вы можете узнать начальный список плагинов.
id плагина
Посылается ядром каждый раз, когда выгружается какой-нибудь плагин.
delay: Integer (опционально)
Инициирует завершение приложения. Все плагины будут выгружены. Отменить завершение приложения штатными средствами нельзя. Параметр delay позволяет задать задержку в миллисекундах.
srcTag: String
dstTag: String
priority: Integer
Регистрирует новую альтернативу dstTag для тега srcTag с приоритетом priority. Плагин core подписывается на srcTag. При поступлении сообщения оно будет перенаправлено на srcTag (при этом отправитель останется таким как был, а не изменится на core). Если существует несколько альтернатив для srcTag, то будет выбрана та, которая имеет наибольшее числовое значение приоритета.
Плагин core запоминает id плагина, который зарегистрировал альтернативу. При его выгрузке все альтернативы, которые он зарегистрировал, будут удалены.
Для регистрации нескольких альтернатив можно воспользоваться сообщением с тегом core:register-alternatives. Оно принимает те же данные, но обёрнутые в список (List<Map<String, Object>>).
srcTag: String
dstTag: String
Удаление альтернативы dstTag для тега srcTag. Альтернатива будет удалена только если данное сообщение отправлено тем же плагином, что и зарегистрировал данную альтернативу.
См. также: core:register-alternative.
srcTag: String
dstTag: String
priority: Integer
Изменяет числовое значение приоритета для альтернативы dstTag для тега srcTag. Изменить приоритет альтернативы может любой плагин, а не только тот, который её зарегистрировал.
В нормальных условиях изменять приоритет альтернативы следует только по запросу пользователя.
См. также: core:register-alternative
seq: Object (рекомендуется Integer)
seq: Object
map: Map<String, Object>
Ключи: srcTag
Значения: Map<String, Object>
tag: String (dstTag)
priority: Integer
plugin: String (id плагина)
Служит для запроса списка всех зарегистрированных альтернатив. Полезно для предоставления пользователю возможности настройки приоритетов.
Тег ответного сообщения соответствует идентификатору плагина, пославшего запрос. seq в ответном сообщении равен seq в запросе.
См. также: core:register-alternative
seq: Object (рекомендуется Integer)
seq: Object
path: String
Позволяет плагину получить путь к каталогу, который ему рекомендуется использовать для сохранения своих настроек.
Тег ответного сообщения соответствует идентификатору плагина, пославшего запрос. seq в ответном сообщении равен seq в запросе.
Тег пайпа
Создаёт пайп (конвейер) с указанным именем (тегом). После этого ядро подписывается на указанный тег и реализует некоторую логику. А именно:
Пайп содержит в себе последовательность тегов сообщений, слушатели которых по сути дела являются обработчиками пайпа. При приходе сообщения на тег пайпа, его данные будут по очереди отправлены на каждый тег, который составляет конвейер. При этом действует следующее соглашение: данные проходящие через пайп обязательно имеют тип Map<String, Object> (если отправить на пайп данные другого типа, то они будут преобразованы в Map с ключом "data" равным исходному значению), этот Map содержит ключ "seq", который никто не должен изменять кроме плагина core. Каждый обработчик конвейера должен отправлять данные снова в конвейер (на тот же тег), при этом допустимо изменять все ключи кроме "seq". Когда весь конвейер пройден, итоговые данные отправляются в сообщении с тегом равным идентификатору плагина, который отправил сообщение в пайп.
Тег пайпа
Уничтожает указанный пайп. Пайп должен быть создан тем же плагином, что и уничтожает его. При выгрузке плагина все созданные им пайпы уничтожаются автоматически.
pipe: String
tag: String
Добавляет тег tag в конец конвейера pipe. Теперь вы обязаны слушать тег tag и пересылать данные каждого сообщения с тегом pipe. При этом данные всегда имеют тип Map<String, Object> и вы можете менять любые его ключи кроме seq и pipeReplyTo.
pipe: String
tag: String
Добавляет тег tag в начало конвейера pipe. Требования те же, что и для core:append-pipe-stage.
pipe: String
tag: String
Удаляет тег tag из конвейера pipe. Теперь вы не обязаны слушать тег tag. Также теги автоматически удаляются из конвейера при выгрузке плагина, который их добавил. Удалить тег из конвейера может только тот плагин, который его туда добавил.
seq: Object (рекомендуется Integer)
delay: int или long (опционален)
seq: Object
Вместо использования стандартных Java-таймеров или таймеров, предоставляемых различными тулкитами, рекомендуется пользоваться этим сообщением, которое использует самый эффективный способ, доступный в текущей среде.
Спустя delay миллисекунд после отправки сообщения будет вызвана callback-функция, передаваемая третьим параметром метода sendMessage (см. описание Groovy Plugin API). Ей будет передан тот же seq, что и при вызове.
Если указать seq тайматута, который уже идёт, то он будет перезапущен с новым временем. Если время отрицительное (по умолчанию delay равен -1), то таймаут будет отменён.
Все события, связанные с мышью, возвращают как минимум 4 параметра:
- screenX / screenY - координаты мыши относительно экрана;
- nodeX / nodeY - координаты мыши относительно элемента, над которым она находится.
Пользователь кликнул левой кнопкой мыши по персонажу
Пользователь кликнул правой кнопкой мыши по персонажу
Пользователь кликнул средней кнопкой мыши по персонажу
Пользователь дважды кликнул по персонажу
Пользователь перемещает курсор по персонажу
Пользователь прокручивает колёсико мышки над персонажем. В качестве параметра delta приходит либо 1 при прокрутке вниз, либо -1 при прокрутке вверх.
Пользователь кликнул по сообщению левой кнопкой мыши
Пользователь кликнул по сообщению правой кнопкой мыши
Пользователь кликнул по сообщению средней кнопкой мыши
Пользователь дважды кликнул по сообщению кнопкой мыши
Пользователь перемещает курсор по сообщению
Пользователь прокручивает колёсико мышки над сообщением. В качестве параметра приходит либо 1 при прокрутке вниз, либо -1 при прокрутке вверх.
text: String
characterImage: String (опционально)
timeout: Integer (опционально)
priority: Integer (опционально)
Вывести текстовое сообщение. При этом на время показа сообщения может быть изменена картинка персонажа на указанную (из скина, если текущий скин не содержит данного изображения, будет использовано normal).
Сообщение будет закрыто по клику пользователя или по таймауту (указывается в миллисекундах, нулевое значение отключает автоматическое закрытие сообщения, при отсутствии значения будет использоваться значение по умолчанию, которое можно настраивать пользователь).
При закрытии сообщения картинка персонажа изменится на текущую.
Для сообщения можно задать приоритет. По умолчанию он равен 1000. Каждое сообщение висит как минимум половину своего таймаута. Если посылается запрос на отображение сообщения, когда уже висит "облачко" с другим, то программа руководствуется следующими правилами:
- если приоритет нового сообщения больше, чем предыдущего, то через половину таймаута старого новое отображается сразу поверх старого;
- если приоритет равен или ниже, то сообщение будет отображено после полного таймаута предыдущего и всех остальных более приоритетных сообщений в очереди;
- если приоритет равен нулю или меньше, то сообщение считается неважным и не будет запланировано к отображению вообще.
Рекомендуется вместо прямого обращения к плагину gui использовать альтернативу DeskChan:say.
Название картинки
Изменить текущую картинку персонажа. Используется, если никакое сообщение не отображается, либо текущее сообщение не переопределяет картинку.
Если картинка с таким именем не содержится в текущем скине, то используется normal.
Путь к скину
Меняет текущий скин. Изменение будет сохранено в настройках.
absolute: float
или
relative: float
save: boolean (опционально)
Изменяет непрозрачность персонажа. Чем меньше значение, тем прозрачнее изображение. Значение можно задать абсолютно (absolute) либо относительно текущего уровня (relative).
Дополнительный параметр save позволяет указать, нужно ли сохранить изменение в настройках. По умолчанию все изменения выполняются только в рамках текущей сессии.
scaleFactor: float
или
zoom: float
или
width: int
или
height: int
save: boolean (опционально)
Увеличивает или уменьшает размер спрайта в зависимости от заданного параметра, в качестве которого может быть либо абсолютное значение масштаба (scaleFactor), либо его относительное изменение в зависимости от текущего значения (zoom), либо конкретные значения ширины (width) или высоты (height).
Параметры рассматриваются в указанном порядке. Если будет передано несколько аргументов, то будет использован первый совпавший, а остальные будут проигнорированы. Поэтому, например, ширина и высота не могут использоваться одновременно! Программа следит за сохранением пропорций изображения.
Дополнительный параметр save позволяет указать, нужно ли сохранить изменение масштаба в настройках. По умолчанию все изменения выполняются только в рамках текущей сессии.
name: String
msgTag: String
msgData: Object (опционально)
Регистрирует простое действие. Оно доступно через контекстное меню персонажа. При выборе действия будет послано сообщение с тегом msgTag и msgData (null, если не указать).
Действия автоматически удавляются при выгрузке плагина, который их зарегистрировал.
В будущем способ вызова простого действия пользователем может быть изменён.
Рекомендуется не обращаться напрямую к плагину gui, а использовать альтернативу DeskChan:register-simple-action.
Для регистрации нескольких действий рекомендуется использовать сообщение с тегом DeskChan:register-simple-actions. В качестве данных оно принимает список таких же наборов параметров (List<Map<String, Object>>).
name: String
text: String
Показывает окно сообщения с названием name и текстом text.
name: String
msgTag: String (опционально)
controls: List<Map<String, Object>>
Создаёт или обновляет уже существующую вкладку настроек для плагина, отправившего это сообщение (автоматически удаляется при его выгрузке).
Если указан параметр msgTag, то на вкладку будет добавлена кнопка "Сохранить", которая при нажатии отправляет сообщение с указанным тегом и со значениями элементов управления.
controls является списком элементов управления, которые должны быть размещены на вкладке.
Каждый элемент управления имеет как минимум три параметра:
type: String
id: String (опционально)
label: String (опционально)
type задаёт тип элемента управления (в зависимости от него могут добавлять другие обязательные и опциональные параметры), label позволяет разместить текстовую подпись рядом с элементом управления. Если же у элемента задан id, то при нажатии на кнопку сохранения, он будет добавлен в Map, который будет отправлен в качестве данных сообщения (при этом ключом будет id, а значением - текущее значение элемента управления).
value: String
Самый простой элемент управления. Статичная надпись.
value: String (опционально)
hideText: Boolean (опционально)
Однострочное текстовое поле.
value: Integer (опционально)
min: Integer (опционально)
max: Integer (опционально)
step: Integer (опционально)
onChange: Map<String, Object> (опционально)
msgTag: String
newValueField: String (опционально)
oldValueField: String (опционально)
multiplier: Double (опционально)
data: Map<String, Object> (опционально)
Поле ввода для целых числовых значений.
При заданном параметре onChange при каждом изменении значения поля будет отправляться сообщение на тег msgTag. По умолчанию новое значение в сообщении будет под ключом value, а старое — oldValue, но это можно изменить с помощью параметров newValueField и oldValueField. Также с помощью параметра multiplier можно установить число, на которое реальное значение поля будет умножаться при отправке сообщения. Наконец, в параметре data можно указать различные дополнительные данные, которые следует отправить в сообщении.
value: Double (опционально)
min: Double (опционально)
max: Double (опционально)
step: Double (опционально)
onChange: Map<String, Object> (опционально)
msgTag: String
newValueField: String (опционально)
oldValueField: String (опционально)
multiplier: Double (опционально)
data: Map<String, Object> (опционально)
Поле ввода для числовых значений с плавающей запятой.
Информация о параметре onChange представлена в описании к элементу Spinner.
value: Double (опционально)
min: Double (опционально)
max: Double (опционально)
step: Double (опционально)
onChange: Map<String, Object> (опционально)
msgTag: String
newValueField: String (опционально)
oldValueField: String (опционально)
multiplier: Double (опционально)
data: Map<String, Object> (опционально)
Ползунок по числовой прямой для установки целых или дробных значений.
Информация о параметре onChange представлена в описании к элементу Spinner.
value: boolean (опционально)
Переключатель. Может быть установлен (true) или сброшен (false).
values: List<String>
value: Integer (опционально)
Поле выбора из фиксированного набора вариантов. Значением поля является индекс выбранного элемента.
values: List<String>
value: List<Integer>
Поле выбора из фиксированного набора вариантов. При этом пользователь имеет возможность выбрать несколько элементов сразу. Значением является список индексов выбранных элементов.
value: String
msgTag: String
msgData: Object (опционально)
Кнопка. Посылает сообщение с тегом msgTag и данными msgData при нажатии.
value: String (опционально)
initialDirectory: String (опционально)
filters: List<Map<String, Object>> (опционально)
Поле выбора файла. Можно указать начальную директорию.
Для фильтрации по расширениям есть параметр filters. Он принимает список, из которого пользователь сможет выбрать нужный ему фильтр. Каждый фильтр представляет собой словарь из двух значений:
- description (String) — описание фильтра;
- extensions (List<String>) — список расширений в формате
*.<расширение>
.
value: String (опционально)
format: String (опционально)
Поле для выбора даты.
Несмотря на то, что значение в поле отображается в
зависимости от локали, по умолчанию его следует задавать
и получать в формате ISO (например, 2017-04-09). Формат
можно изменить с помощью параметра format. Например
для РФ (9.04.2017), это будет d.M.y
.
name: String (опционально)
msgTag: String (опционально)
controls: List<Map<String, Object>>
Создаёт и отображает обычное отдельное окно с указанными элементами, список которых перечислен для сообщения gui:setup-options-tab.
title: String (опционально)
multiple: boolean (опционально)
initialDirectory: String (опционально)
initialFilename: String (опционально)
saveDialog: boolean (опционально)
filters: List<Map<String, Object>> (опционально)
seq: Object
path: String
или
paths: List<String>
Отображает диалоговое окно, позволяющее пользователю выбрать файл(ы). В ответ приходит строка (path) или список строк с путями (paths) до выбранных файлов. Параметр multiple как раз отвечает за то, можно ли выбирать несколько файлов. По умолчанию он равен false.
С помощью title можно изменить заголовок окна, используя initialDirectory — начальную директорию, а с помощью initialFilename — предложить название файла. Для выбора несуществующего файла или файла, разрешённого пользователем к перезаписи, установите saveDialog в true.
Для фильтрации по расширениям есть параметр filters. Он принимает список, из которого пользователь сможет выбрать нужный ему фильтр. Каждый фильтр представляет собой словарь из двух значений:
- description (String) — описание фильтра;
- extensions (List<String>) — список расширений в формате
*.<расширение>
.
title: String (опционально)
initialDirectory: String (опционально)
seq: Object
path: String
Отображает диалоговое окно, позволяющее пользователю выбрать папку. В ответ приходит строка с путём (path) до выбранной директории. С помощью параметра title можно изменить заголовок окна, а используя initialDirectory — начальную директорию.
purpose: String (опционально)
Выбирает из списка доступных подходящих фраз одну с заданным назначением, компонует её и отправляет в DeskChan:say. Список назначений можно посмотреть в описании системы характеров.
type: String, возможные значения - character, emotional
feature: String
multiplier: Float
Оказывает влияние на характер (в случае type=character) или на эмоциональное состояние (в случае type=emotional). Значение feature указывает, на какую компоненту системы оказывается влияние. В случае type=character стандартное значение - sympathy, В случае type=emotional стандартное значение - happiness, полный список можно посмотреть в описании системы характеров. multiplier - число, на которое сдвигается шкала.
priority: Integer
Сохраняет плагин, отправивший сообщение, как перк, к которому впоследствие будут отсылаться запросы с целью его внедрения в систему характеров. На данный момент перки могут только редактировать текст сообщения перед посылкой его в очередь на отображение. Перки получают ообщения в зависимости от приоритета, указанного при регистрации, от самого большего к самому меньшему.
Удаляет плагин из списка перков.
url: String
filename: String
Обращается к JSON файлу фраз по указанной в url ссылке и сохраняет его в качестве списка фраз в формате xml под названием filename.quotes.
url: String
filename: String
На этот тип сообщений плагины должны присылать ответы на запросы, присланные им от talking_system. Подробнее не будет, лезьте в код, конкретно файл PerkContainer.java.
Запросы к перкам от talking_system. Подробнее не будет, лезьте в код, конкретно файл PerkContainer.java.
Рекомендуется писать плагины на языке программирования Groovy. Плагином является каталог, содержащий файл plugin.groovy. В этом же каталоге можно также сохранить какие-либо статичные файлы, необходимые плагину. Для хранения настроек и кеша следует использовать другой каталог (см. core:get-plugin-data-dir).
Скрипт должен отправить все необходимые сообщения и подписаться на все необходимые ему сообщения.
Помимо всех стандартных функций Java ему доступны следующие функции DeskChan Plugin API:
void sendMessage(String tag, Object data);
void sendMessage(String tag, Object data, ResponseListener responseListener);
void addMessageListener(String tag, MessageListener listener);
void removeMessageListener(String tag, MessageListener listener);
void addCleanupHandler(Runnable handler);
sendMessage позволяет отправить сообщение с тегом tag и данными data. Версия с тремя аргументами преобразует data в Map, если он им не является (при этом прошлое значение data будет сохранено с ключом "data"). В этот словарь будет добавлено числовое поле seq, которое будет монотонно возврастать с каждым вызовом данной функции (то есть его можно считать уникальным первые несколько миллиардов вызовов sendMessage). В качестве последнего аргумента можно передать лямбда-функцию, которая будет вызвана, когда будет получен ответ с тем же seq и с тегом соответствующим id текущего плагина. Лямбда-функция должна принимать два аргумента: sender (String) и data (Object).
addMessageListener и removeMessageListener соответственно добавляют и удаляют подписку на сообщение с указанным тегом (плагин может подписываться несколько раз на одно и то же сообщения разными слушателями). Слушателем может выступать лямбда-функция, принимающая три аргумента: sender (String), tag (String), data (Object). Можно использовать одного и того же слушателя для разных сообщений (различать их по аргументу tag, если требуется). При выгрузке плагина все его слушатели удаляются автоматически.
addCleanupHandler позволяет зарегистрировать обработчик выгрузки плагина. Он должен уничтожить те объекты, которые в ином случае уничтожены не будут. В качестве него может выступать лямбда-функция без аргументов.
Данное API не является стандартным, запилено другим человеком и может быть изменено в будущем.
По сути тут всё аналогично вышеописанному Groovy Plugin API. Обратить внимание нужно на буквально несколько моментов:
- В отличие от Groovy-плагинов, JavaScript-плагины не являются наследниками никаких классов и,
соответственно, не имеют встроенных методов
sendMessage()
,addMessageListener()
и т. п. Но в исполяемую среду "инжектится" специальный объект — так называемая шина (bus) — с помощью которой плагин "общается" с приложением. Она предоставляет ему описанные в предыдущем разделе методы. - Типы данных на ходу преобразуются между типами одного языка в другой. В случае каких-либо проблем
стоит иметь это в виду, поскольку возможны различные проблемы (в том числе по причине ошибок в компиляторе Nashorn).
Для примера использования API смотрите приложенный плагин-пример (test_javascript). - У шины есть нестандартный метод
say()
, представляющий собой метод-сокращение для облегчения вывода сообщений. Он поддерживает все опциональные параметры сообщенияgui:say
(priority, timeout, characterImage). Также для строк с символами, выходящими за рамки таблицы ASCII, не забывайте указывать ихu"юникодную природу"
! - Методы
getDataDirPath()
,getPluginDirPath()
иgetRootDirPath()
возвращают не объекты Java-класса Path, а обычные строки.