
— История изменений плагинов
— Автоматическое обновление плагина при обновлении версии игры
— Разработка плагинов на localhost
— Неограниченное количество веток, например, для нестабильных версий
— Зависимости (плагин А автоматически подключает плагин Б)
— Встроенная возможность делать паки (следствие предыдущего пункта)
— Легкое изменение любой части клиента игры
— Полный административный контроль авторов игры над всеми плагинами
— Поиск по базе плагинов
— При этом простая установка юзером и удобная работа для плагинописцев.
Игра
Генералы — это тактическая карточная игра. Думаю, многие знакомы с Magic: The Gathering. Основной геймплей — это поле боя 5*3, где задача — карточками танков, взводов и приказов уничтожить штаб противника.

Кроме экрана боя, есть такие экраны, как «Ангар» для выбора колоды, с которой вы пойдёте в бой, «Дерево исследований», “Редактор колод” и так далее.

Объекты, с множеством анимаций (вроде карусели в ангаре, дерева исследований и, конечно, боя) написаны на LibCanvas и отрисовываются на html5 canvas. Интерфейсы попроще пишутся на html.
Про систему плагинов
Плагины — однозначно полезный функционал.
Во-первых, хардкорные игроки могут использовать их для улучшения собственного впечатления от игры.
Во-вторых, сами по себе разработчики могут почерпнуть кое-какие идеи.
В-третьих, некоторые плагинописцы уже работают у нас в команде.
В-четвёртых, некоторые уязвимости были найдены именно благодаря сторонним авторам.
И хотя официального релиза у нас ещё не было, но система плагинов работает уже полтора года. Я долго думал над тем, какими они должны быть. И ниже я расскажу историю, как и к чему мы пришли в итоге.
Конечно, можно написать API, но мне не нравилась ограниченость такого подхода фантазией программиста и увеличенными затратами на поддержку. Хотелось иметь возможность изменить любую часть клиента игры.
К счастью, это довольно просто по двум причинам:
— Клиент очень тонкий и занимается только отображением — вся логика вынесена на сервер, который никак не подвергается влиянию плагинами
— Все чувствительные операции вынесены на сторонние сервера — авторизация (где мог бы утечь пароль) и платежка (где юзер мог потерять деньги).
В итоге у нас остался только чистый клиент игры, который можно безопасно менять.
Имеется три способа изменения поведения игры:
1. Использование ограниченного API
При создании плагина ему передается инстанс объекта
Wotg.Plugins.Simple
с базовыми методами, которые позволяют выполнять самые простые операции — подмена картинок, смещение элементов, изменение звуков и т. д. Такие операции используются для простых плагинов:
2. Подписка на события
Также у нас можно подписаться на огромное количество событий. Под событиями мы подразумеваем получение сообщения с сервера, нажатие кнопки, открытие нового экрана. Это позволяет реагировать на соответствующие события и, например, при нажатии кнопки «пробел» атаковать врага всем имеющимся арсеналом, как в плагине «Катюша».
3. Агрессивное изменение
Это самый сложный но и самый глубокий метод ( если вы понимаете о чём я ;) ). Он позволяет изменить любой метод любого класса с возможностью вызова предыдущего варианта. Для примера посмотрите ниже часть плагина, которая позволяет сохранять реплеи на стороннем сервере. В этом случае изменяется метод save у ReplayManager.
plugin.refactor( Wotg.Utils.ReplayManager, {
save: function method (battle) {
method.previous.call(this, battle);
var replay, xhr;
replay = this.getCompiledDataFrom(battle);
xhr = new XMLHttpRequest();
xhr.open("POST", MY_OWN_REPLAYS_SERVER, true);
xhr.send(
"player=" + replay.player.name +
"&opponent=" + replay.opponent.name +
"&replay=" + JSON.stringify(replay)
);
}
});
Это дает возможность изменить любое поведение, вплоть до написания нового функционала, как, например, внутриигровой генератор карт:

Вот такая она с точки зрения кода плагина. Но как все это организовать в плане подключения?
История
Изначально (ещё пару лет назад) было решено использовать встроенные в браузер аддоны и официальные магазины вроде chrome.google.com/webstore и addons.mozilla.org/uk/firefox. Но с этим были проблемы. Такие плагины было тяжело разрабатывать, пока игра не зарелизилась — их нельзя добавить в магазин и пользователям. В результате игрокам приходилось копипастом добавлять куски кода, вывешенные в теме на форуме игры. Использование unsafeWindow вместо классического window вносило путаницу для разработчиков. А потом с unsafeWindow появились дополнительные запреты. В общем — темные времена и стало ясно, что необходимо куда-нибудь двигаться.
Хотелось чего-то прогрессивного и удобного. И потому было решено использовать GitHub. Один репозиторий на коммиты и пул-реквесты в репозиторий игры. Удобность работы выросла значительно, но возникло две проблемы — очень долгое обновление GitHub Pages и отсутствие возможности удобного администрирования. Зато было очевидно, что направление движения правильное. И уже было понятно, где наша земля обетованная.
GitLab
Мы подняли GitLab на нашем сервере и выделили его полностью под плагины. И это было божественно. Схема следующая:
— Есть пользователи GitLab — наши плагинописцы
— У каждого пользователя есть репозитории — по одному на каждый плагин
— Репозитории могут иметь несколько бранчей. Например, master и unstable. Master включается по умолчанию.
В итоге мы получаем следующие возможности:
— Весь репозиторий находится под нашим административным контролем и имеет общий с нашим сервером Uptime.
— Формат имени
Owner:Title:Branch
. Например, Shock:MyCoolPlugin
— основной плагин, а Shock:MyCoolPlugin:Unstable
— версия для разработки, которая мерджится в мастер по факту полной готовности. Путь к файлу определяется шаблоном gen-git.socapp.net{author}/{title}/raw/{branch}/{title}.js
. Это дополнительно облегчает совместную работу над плагинами — один разработчик ответвляет от репозитория другого, получает плагин с таким же именем, но иным автором. Тот вносит свои изменения, может даже дать установить свой плагин пользователям, а потом создает пул-реквест в основной плагин.— При установке информация о плагине записывается в localStorage и его основной файл подключается каждый раз после загрузки всех классов но до вызова точки входа.
— Каждый плагин имеет версию игры, для которой он предназначен, и при выходе обновления автоматически выключается, пока автор не изменит версию в соответствующей ветке, и тогда плагин снова автоматически включится для всех пользователей, у которых он установлен. При этом следующую версию можно заранее подготовить на супертесте и просто во время выхода обновления вмерджить её через веб-интерфейс одной кнопкой.
— В коде плагина достаточно написать require, и автоматически подтянутся необходимые плагины (зависимости). Они подтянутся в корректном порядке и будут доступны из тела плагина как показано ниже.
— Также можно при помощи include включать дополнительные классы плагина. Приблизительно так:
new Wotg.Plugins.Simple({
version: '0.6.0',
require: [ 'Another:Plugin' ],
include: [ 'AnotherClass' ],
}, function (plugin, events, required) {
console.log( required['Another:Plugin'] ); // Ссылка на необходимый плагин
console.log( plugin.included['AnotherClass'] ); // Ссылка на необходимый класс
});
— При помощи команды
plugin.addStyles( 'my.css' )
подключить свои стили.— Каждый плагин имеет свои собственные настройки, которые можно получить командой
plugins.getConfig( 'index' )
.— Самые простые плагины можно изменять через веб-интерфейс GitLab, а более сложные благодаря
git clone
разрабатывать локально. Для этого достаточно по шаблону сконфигурировать nginx Благодаря настройкам в игре, соответствующая директория станет использоваться как репозиторий плагинов. И тогда любое изменение в директории подключенного плагина будет отображаться без коммитов в игре разработчика.— У GitLab есть API. Был зарегистрирован фейковый пользователь и благодаря его приватному токену любой веб-клиент игры может отправлять запросы в API. Это позволяет сделать поиск плагинов, валидацию названий и т. п.
# plugins add Test:CardCreate
No such plugin. Did you mean:
- Shock:CardCreate
# plugins add Test:Ca
Min plugin title length is 4: Test:Ca
# plugins add Test:Card
No such plugin. Did you mean:
- Shock:CardCreate
# plugins add Test:Exa
Min plugin title length is 4: Test:Exa
# plugins add Test:Exam
No such plugin. Did you mean:
- Shock:Example
- Isk1n:Example
# plugins add Shock:Unknown
No such plugin, I dont know, what you want to install
# plugins add Shock:Example:Test213
No such plugin. Did you mean:
- Shock:Example:master
- Shock:Example:new-test
Console vs GUI
На данный момент всё управление и установка плагинов производится через внутриигровую консоль, которая открывается по
Ctrl ~
. Всем достаточно удобно (хотя иногда пользователи спрашивают, где тильда). Но в далёких планах есть создание GUI — из localStorage получаем список установленных плагинов, а благодаря GitLab API реализуем их поиск и отображение информации о них.Если кому интересно — можно почитать документацию для разработчиков, посмотреть раздел плагинов, или посмотреть как всё это выглядит в WoT: Generals.
Я специально сюда не включил реализацию именно в плане кода, т. к. она довольно проста. Для меня, самое интересное — это именно использование GitLab как пакетного менеджера. Но если есть вопросы к технической реализации или к идее — жду в комментариях.