MMO на WebRTC

Кожен програміст, який написав своє MMORPG дуже швидко стикається з однією поширеною проблемою. Найбільш перехожений шлях для організації мережі в клієнт-серверних іграх - це зірка, де центральний вузол є сервером, а аркуші - це клієнти.

Така організація має свої незаперечні переваги, наприклад синхронізація ігрових станів клієнтів на сервері, простота в реалізації і майже фіксовані затримки до користувача. Там де є плюси, зазвичай мешкають і мінуси - це обмежена пропускна здатність сервера і досить великі затримки, якщо клієнт знаходиться далеко від вашого сервера. Як з ними боротися детально написано в блозі 0fps.net і є можливість ці проблеми досить ефективно вирішувати залишаючись в улюбленій «зірочці» - купити більше серверів, але що робити якщо ви студент ви обмежені у фінансах і горизонтальне/вертикальне масштабування для вас не варіант?

Не беріться за створення MMORPG

Як відомо, в кожному жарті є частка жарту - якщо у вас немає достатньо ресурсів підтримувати game-dev комманду або ви працюєте над грою один, краще переглянути свої цілі. Коли я починав робити мережевий шутер Auxilium (кдпв з нього), мені було зрозуміло, що доведеться якось вирішувати проблему з масштабуванням і очевидним рішенням, яке використовується в багатьох мережевих іграх було введення ігрових сесій або «боїв» - в цьому випадку можна дуже просто розподіляти ці бої по фізичних машинах і тримати навантаження, але це вже була не класична MMORPG з відкритим світом. Далі послідували три помилки, після яких розробка гри зупинилася:

Першою помилкою було вважати, що хороші сервери можна знайти за осудну (для мене) суму. Початкові заміри продуктивності гри на Node.JS показали, що один процес витягує гру людей на 20-25. Після цього починається просідання eventloop'a і нестатутні лаги через затримку розрахунку фізики на сервері. Так як за 350 р. на місяць я знайшов VPS з 250 Mb пам'яті, то більше двох процесів з грою було не запустити (при старті процес займав ауд 100 Mb, так, можна було закрутити гайки і ужатися). І того 50 гравців. Не дуже то вже й Massive виходило.

Другою помилкою було розробляти гру не одному, а з командою (дев'ять жінок можуть народити одну дитину за місяць, так?). Є хороша стаття про те як зробити MMORPG - «Ми починали втрьох, але вже до кінця першого року розробки я залишився один». Так і вийшло.

Ну і третьою помилкою було вважати, що з TCP можна миритися. Ми взяли за основу WebSockets і додали в нього бінарне стиснення JSON в надії, що цього вистачить. Як знають багато хто, TCP - це протокол з гарантованою доставкою пакетів в тому порядку, в якому вони були відправлені, з цього якщо один пакет загубиться, то для гравця гра замре на час, поки цей пакет не перевідправлять. Найгірше, що після цього прокрутяться всі пакети, які стояли в черзі за ним - знову ж таки можна передбачити збереження затримок (додавати їх штучно), але додавання ювелірних милиць заради милиць (робота з TCP замість UPD) вірна ознака неправильного шляху.

Що хотіли і що майже вийшло (зліва направо):

Але досить про минуле - що ж змінилося і куди переклали граблі, на які хочеться наступити ще раз?

WebRTC и STCP

Переосмисливши свої помилки як розробника, я вирішив ще раз прочесати поле технологій, які можна використовувати для створення MMORPG в браузері. Для мене все ще принципово залишатися в рамках браузерів, так як це дозволяє дуже легко запускати експерименти нічого не встановлюючи (тим більше, що вже і Unreal Engine 4 готується до виходу в браузерному вигляді).

Як виявилося, за час розробки світ на місці не стояв і новою гарячою темою став WebRTC - всі почали робити вбивць skype і chatroulette, але що дійсно привернуло увагу це пункти unreliable і unordered в використовуваному протоколі STCP - а це означає, що проблема номер три вирішена! Але не без обмежень.

Для роботи WebRTC потрібен так званий сигнальний сервер, який відповідатиме за початкове з'єднання двох браузерів за STCP шляхом передачі повідомлень від STUN серверів. Це зроблено для обходу NAT і про це можна докладніше прочитати в статті "Як ми робили сервіс на WebRTC" і "WebRTC in the real world: STUN, TURN and signaling». З останньої статті для себе я виніс те, що якщо ви надаєте WebRTC сервіс за гроші - то ви в пеклі (потрібно домагатися 100% роботи з усіх мереж). Добре, що це не наш випадок.

Тепер архітектура нашої клієнт-серверної гри може виглядати ось таким чином:

Як видно, вся важка взаємодія перекладена з сервера на клієнтів. Такий підхід привабливий, але він вносить вельми не тривіальні завдання для вирішення:

  1. Чи оптимально використовувати повносв'язний граф
  2. Як синхронізувати стан гри для всієї мережі
  3. Що робити з нечесними гравцями

Якщо на два останні питання відповідь частково залежить від ігрової моделі, яку ви розробляєте, то на перше питання можна шукати відповідь для загального випадку. Проте цікаво прочитати про huntercoin - проект, що використовує аналог bitcoins для синхронізації гри, хоча це не підходить для динамічних моделей і зобов'язує мати повну синхронізацію всіх транзакцій (якщо я правильно розумію, як працює bitcoin'и). Інші міркування щодо цих пунктів я залишу для наступної статті.

Для перевірки гіпотези про повний графі (а так само стрес-тест сервера) нам буде потрібна маленька демо-гра, яка буде вміти заміряти latency між клієнтами. Якщо виявиться так, що прямі з'єднання завжди швидше передачі повідомлення через іншого клієнта - то повний граф, так повний граф (у чому я сумніваюся).

MMO — Mouse Multiplayer Online

Demo: mmo.jit.su (Експеримент завершено, результат зафіксовано внизу статті)

Майже рік тому світ побачив crowd-sourced music video. Якщо ви пропустили його, саме час перерватися і подивитися (а там є на що). Ідея проста - записуємо рухи курсора під час відео і додаємо його до вже накопичених. У підсумку отримуємо досить цікавий відеоряд з курсорів (на початку мені здалося, що синхронізація відбувається в реальному часі, але ні). Подивившись цей кліп ще раз - мені прийшла ідея для демо-гри - будемо рухати курсорами.

Для реалізації нам потрібно щось полегшує роботу з сигнальним сервером і створенням з'єднань між клієнтами. Хорошим варіантом є Peer.js - який надає готовий сигнальний сервер (правда заснований на restify) і навіть хостит публічний сигнальний сервер.

Весь код клієнтської частини вміщується в 100 рядків JavaScript, серверна частина на Node.JS і того менше - 55. Наводити їх у статті я сенсу не бачу, оскільки вони лежать у відкритому доступі і досить прості в написанні. Сам код викладено під ліцензією MIT і я сподіваюся він допоможе початківцям зробити свою breakable-toy.

Що в підсумку?

Після збору невеликої статистики з гри я клятвено обіцяю викладу відповідь на питання, що мене цікавить. Хоча інтуїція і говорить, що оскільки ми спираємося на звичайну мережеву інфраструктуру, яка прагне вибирати оптимальний шлях для відправки пакетів може виявитися так, що цей шлях швидше збігається з пересиланням повідомлень між клієнтами гри, ніж створенням нового з'єднання (і чим більше мережа, тим більше впевненість у цьому). З цього широкомовна розсилка дублюватиме пакети, але статистика покаже.

Результат

Як видно, щось пішло не так. У нас є багато peer-to-peer гравців (зліва-знизу), а так само багато компонент зв'язності і з великим числом учасників. Найбільша компонента знаходиться праворуч знизу і вона, очевидно, не є повним графом.

Чому так сталося? Може бути дивацтва в реалізації розподілу ідентифікаторів в Peer.js, так як там використовується Math.random - хоча це і малоймовірно для 2300 нод. Швидше за все це особливості роботи протоколу WebRTC - адже ясно, що нескінченного числа з'єднань не можна собі дозволити.

Під час експерименту було помічено (може бути тільки у мене), що одночасно рухалися курсорів 5 - 10, що відсікає можливість використовувати повний граф для синхронізації великої мережі. Як робити це не через broadcast - це матеріал для окремої статті (якщо не серії статей). Всім спасибі за участь!

Тільки зареєстровані користувачі можуть брати участь в опитуванні. Увійдіть, будь ласка.

Чи можна зробити децентралізовану MMORPG?

25.8% Можна зробити клон WoW 161

44.71% Можна, але з істотними обмеженнями 279

29.49% Не можна - грати в це буде не можливо 184

Проголосували 624 користувачі. Утримався 301 користувач.