Користувальницькі плагіни в JavaScript іграх

Хто не знає, Wargaming зараз розробляє тактичну карткову гру WoT: Generals. Web-версія написана на PHP, використовуються LibCanvas і AtomJS. Я брав безпосередню участь і хочу розповісти про функціонал, який мені здається цікавим і може бути корисним у всіх веб-іграх. А саме система плагінів гри, яка надихалася пакетними менеджерами в Лінуксі і має наступні можливості:

— Історія змін плагінів
— Автоматичне оновлення плагіна при оновленні версії гри
— Розробка плагінів на 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
— версія для розробки, яка мерджится у майстер за фактом повної готовності. Шлях до файлу визначається шаблоном
https://gen-git.socapp.net/{author}/{title}/raw/{branch}/{title}.js
. Це додатково полегшує спільну роботу над плагінами — один розробник ответвляет від репозиторію іншого, отримує плагін з таким же ім'ям, але іншим автором, вносить свої зміни, може навіть дати встановити свій плагін іншим користувачам, а потім створює пул-реквест в основний плагін.
— При установці інформація про плагіні записується в localStorage і його основний файл підключається кожен раз після завантаження всіх класів але до виклику точки входу.
— Кожен плагін має версію гри для якої він призначений, і при виході оновлення автоматично вимикається, поки автор не змінить версію у відповідній гілці і тоді плагін знову автоматично включиться для всіх користувачів, у яких він встановлений. При цьому наступну версію можна заздалегідь підготувати на супертесте і просто під час виходу оновлення вмерджить її через веб-інтерфейс однією кнопкою.
— У коді плагіна достатньо написати
require
і автоматично підтягнуться необхідні плагіни (залежності). Вони підтягнуться в коректному порядку і будуть доступні з тіла плагіна як показано нижче.
— Так само можна за допомогою
include
включати додаткові класи з плагіна. Приблизно так:

new Wotg.Plugins.Simple({
версія: '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 як пакетного менеджера. Але якщо є питання до технічної реалізації або до ідеї — чекаю в коментарях.

Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.