![](https://habrastorage.org/files/5de/93f/862/5de93f862ba8475abfd51ecfd752708b.jpg)
— История изменений плагинов
— Автоматическое обновление плагина при обновлении версии игры
— Разработка плагинов на localhost
— Неограниченное количество веток, например, для нестабильных версий
— Зависимости (плагин А автоматически подключает плагин Б)
— Встроенная возможность делать паки (следствие предыдущего пункта)
— Легкое изменение любой части клиента игры
— Полный административный контроль авторов игры над всеми плагинами
— Поиск по базе плагинов
— При этом простая установка юзером и удобная работа для плагинописцев.
Игра
Генералы — это тактическая карточная игра. Думаю, многие знакомы с Magic: The Gathering. Основной геймплей — это поле боя 5*3, где задача — карточками танков, взводов и приказов уничтожить штаб противника.
![](https://habrastorage.org/files/b6a/e50/157/b6ae50157c394eb2ae3eff71a90f97ba.jpg)
Кроме экрана боя, есть такие экраны, как «Ангар» для выбора колоды, с которой вы пойдёте в бой, «Дерево исследований», “Редактор колод” и так далее.
![](https://habrastorage.org/files/296/50a/f2e/29650af2ecd04478af848bce26be6d3f.jpg)
Объекты, с множеством анимаций (вроде карусели в ангаре, дерева исследований и, конечно, боя) написаны на LibCanvas и отрисовываются на html5 canvas. Интерфейсы попроще пишутся на html.
Про систему плагинов
Плагины — однозначно полезный функционал.
Во-первых, хардкорные игроки могут использовать их для улучшения собственного впечатления от игры.
Во-вторых, сами по себе разработчики могут почерпнуть кое-какие идеи.
В-третьих, некоторые плагинописцы уже работают у нас в команде.
В-четвёртых, некоторые уязвимости были найдены именно благодаря сторонним авторам.
И хотя официального релиза у нас ещё не было, но система плагинов работает уже полтора года. Я долго думал над тем, какими они должны быть. И ниже я расскажу историю, как и к чему мы пришли в итоге.
Конечно, можно написать API, но мне не нравилась ограниченость такого подхода фантазией программиста и увеличенными затратами на поддержку. Хотелось иметь возможность изменить любую часть клиента игры.
К счастью, это довольно просто по двум причинам:
— Клиент очень тонкий и занимается только отображением — вся логика вынесена на сервер, который никак не подвергается влиянию плагинами
— Все чувствительные операции вынесены на сторонние сервера — авторизация (где мог бы утечь пароль) и платежка (где юзер мог потерять деньги).
В итоге у нас остался только чистый клиент игры, который можно безопасно менять.
Имеется три способа изменения поведения игры:
1. Использование ограниченного API
При создании плагина ему передается инстанс объекта
Wotg.Plugins.Simple
с базовыми методами, которые позволяют выполнять самые простые операции — подмена картинок, смещение элементов, изменение звуков и т. д. Такие операции используются для простых плагинов:![](https://habrastorage.org/files/5c0/426/3df/5c04263dfa8c415e9c95799b55838a55.jpg)
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)
);
}
});
Это дает возможность изменить любое поведение, вплоть до написания нового функционала, как, например, внутриигровой генератор карт:
![](https://habrastorage.org/files/d6c/c62/c2f/d6cc62c2fd984d38b358331bd1298524.jpg)
Вот такая она с точки зрения кода плагина. Но как все это организовать в плане подключения?
История
Изначально (ещё пару лет назад) было решено использовать встроенные в браузер аддоны и официальные магазины вроде 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
— версия для разработки, которая мерджится в мастер по факту полной готовности. Путь к файлу определяется шаблоном https://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 как пакетного менеджера. Но если есть вопросы к технической реализации или к идее — жду в комментариях.
kstep
Зашёл на веб-версию, просит зарегаться. Блин, опять выдумывать логин-пароль. О, есть кнопка логина через G+! Жму, разрешаю права, и… получаю ту же форму регистрации с предзаполненными полями мыла и логина, но надо всё равно придумывать пароль. Шта??? Всё, на этом игра для меня закончилась, конверсия не произошла.
greedykid
У меня получилось не через создание аккаунта, а через линк «Войти», пароль не потребовали —
Два:
Три:
StGeass
Идея восхитительна!
Вы с самого начала задумывали систему плагинов, учитывая её при разработке, или пришли к ней позже?
TheShock
Спасибо) Я с самого начала хотел плагины, но пришёл к ним позже.
На самом деле много кода пришлось переписать (эволюционно, мелкими итерациями) и ещё много — переписывается.
Основная задача (для возможностей «Агрессивного изменения») — это максимально минимальные методы. Чем меньше — тем лучше. (прям как в книгах пишут). Чем меньше метод — тем меньше влияния оказывает изменение автора плагина и легче переносится на следующую версию. Потому у нас практически не создаются функции в методах, а сначала мы перешли на bind, а потом вообще на подход с передачей объекта с нужными методами. Пример:
Но, по сути, когда прикрутилась система плагинов — это всё постепенно улучшалось в рамках разработки задач и ежедневного рефакторинга старого кода.
Nadoedalo
Эм… Ждите ботов на свою игру :D Которые и играть будут лучше чем 51% игроков =)
TheShock
Боты — не вопрос API. Точно так же можно написать через распознавание картинок (благо, достаточно сравнивать по паттерну) и кликам.
Nadoedalo
Распознавание картинок дело сложное и неблагородное. А вот подключится напрямую к методам это уже очень и очень круто. И никакая обфускация тут не поможет.
Просто я имею небольшой опыт написания ботов, и вот эта игра выглядит как очень хорошо и легко автоматизируемая, причём абсолютно безпалевно.
Не представляю себе как WG будет с этим бороться.
Zenitchik
Почему бы нет? Первая игра, в которой будут развлекаться программисты, а не задроты.
TheShock
На самом деле я тоже имею кое-какой опыт в этом и знаю, что обфускация — это вообще не решение))
Кроме парсинга картинок и кликов (я его сказал как крайний случай когда всё остальное закрыто) можно проанализировать запросы-ответы сервера и представляться настоящим клиентом. Без всякого API. Небольшой скриптик, скажем, на node.js.
Я к тому, что вопрос ботоводства и вопрос API — независимые вопросы).
Способы борьбы с ботами, к сожалению, обсуждать не могу.
vGrabko99
жду статьи о технической стороне )
TheShock
Там на самом деле нечего писать — всё самое интересное уже тут, а осталось только очевидное, как на меня. Если есть вопросы — я лучше на них тут отвечу)
vGrabko99
я вообще не могу понять как вы функции подменяете
TheShock
Ну нас самом деле в JavaScript это проще простого. Вот, допустим, есть класс в игре:
Пишем в нашем API подменятор:
Теперь в нашем плагине просто подменяем метод:
Пробуем:
Zenogears
Эхх. Очень нравятся такие речи:
Или вот, например, статья, что Linux Foundation объявила о включении в свои ряды компанию Wargaming.
Пруф. (правда старенький, но я думаю ничего не изменилось, правда ведь?)
Внимание вопрос. Почему компания, входящая в Linux Foundation и берущая идеи из Linux, до сих пор не имеет нативного клиента для своей игры?
Зато какие пафосные речи:
Да, в этом вы проигрываете Gaijin Entertainment, которая просто берет и делает. Хотя-бы клиент под Linux.
Про внутреннюю механику игр и тему доната затрагивать не хочу. Просто как-то не честно к пользователям и игрокам, не находите?
vGrabko99
у них движок не кросс платформенный вроде бы.
TheShock
Я как-бы с Linux прекрасно играю в нашу игру и разрабатываю её. Более того — скриншоты в топике сделаны как раз в Kubuntu.
Zenogears
greedykid
Думаю, вы путаете игру, что обсуждается в этом топике (WoT Generals — карточная игра типа Хартстоуна), и сам WoT. :)
Zenogears
Нет, никогда такого не было!
Я изначально наезжал на Wargaming и WoT, про WoT Generals я ни одним словом не заикнулся :)
TheShock
Так а топик то про WoT Generals.