Pull to refresh
0
ex-Wargaming
Издатель и разработчик free-to-play MMO

Пользовательские плагины в JavaScript играх

Reading time 6 min
Views 12K
Wargaming сейчас разрабатывает тактическую карточную игру WoT: Generals. Веб-версия написана на JS, используются LibCanvas и AtomJS. Я принимал непосредственное участие в разработке и хочу рассказать про функционал, который мне кажется интересным и может быть полезным во всех веб-играх. А именно — о системе плагинов игры, которая вдохновлялась пакетными менеджерами в Linux и имеет следующие возможности:

— История изменений плагинов
— Автоматическое обновление плагина при обновлении версии игры
— Разработка плагинов на 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 как пакетного менеджера. Но если есть вопросы к технической реализации или к идее — жду в комментариях.
Tags:
Hubs:
+17
Comments 20
Comments Comments 20

Articles

Information

Website
lesta.ru
Registered
Founded
Employees
501–1,000 employees
Location
Россия