Як зберегти синхронізовану структуру даних по мережі?


12

Контекст

У грі, над якою я працюю (свого роду крапка та натисніть графічну пригоду), майже все, що відбувається в ігровому світі, контролюється менеджером дій, який структурований приблизно так:

введіть тут опис зображення

Наприклад, якщо в результаті вивчення об'єкта персонаж повинен привітатись, пройтися трохи, а потім сісти, я просто породив наступний код:

var actionGroup = actionManager.CreateGroup();
actionGroup.Add(new TalkAction("Guybrush", "Hello there!");
actionGroup.Add(new WalkAction("Guybrush", new Vector2(300, 300));
actionGroup.Add(new SetAnimationAction("Guybrush", "Sit"));

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

Проблема

Тепер мені потрібно скопіювати цю інформацію в мережі, так що під час сеансу для кількох гравців всі гравці бачать одне і те ж. Серіалізація окремих дій не є проблемою. Але я абсолютно новачок, коли справа стосується мереж, і у мене є кілька питань. Я думаю, що для простоти в цій дискусії ми можемо абстрагувати компонент менеджера дій просто:

var actionManager = new List<List<string>>();

Як слід діяти, щоб вміст вищезазначеної структури даних синхронізувався між усіма гравцями?

Окрім основного питання, у мене також є кілька інших проблем, пов'язаних із цим (тобто всі можливі наслідки цієї ж проблеми вище):

  • Якщо я використовую архітектуру сервера / клієнта (з одним із гравців, що виступає як сервер і клієнт), і один з клієнтів породив групу дій, чи повинен він додати їх безпосередньо менеджеру, чи лише надіслати запит на сервер, що в свою чергу наказать кожному клієнту додати цю групу ?
  • Що з втратами пакетів тощо? Гра є детермінованою, але я думаю, що будь-яка невідповідність послідовності дій, виконаних у клієнта, може призвести до невідповідних станів світу. Як я захищаю від подібних проблем ?
  • Що робити, якщо я додати занадто багато дій одночасно, це не спричинить проблеми для з'єднання? Будь-який спосіб полегшити це?

5
Обов’язково "прочитайте це перше" посилання gafferongames.com/networking-for-game-programmers, яке не дуже технічне, незважаючи на те, що у вас є якийсь код, але воно багатослівне і пояснює ваші варіанти досить добре. Плюс це поставить вас нарівні з усіма, хто прочитав це посилання, і це щасливе місце.
Патрік Х'юз

@PatrickHughes Дякуємо за посилання! Я обов'язково все це прочитаю.
Девід Гувейя

Є деякі пакети, які керують подібними речами для вас. Я не знаю, наскільки це ефективно чи швидко, але цікавий приклад NowJS ( nowjs.com ). З точки зору розробників, ви просто маєте спільні структури даних між клієнтом і сервером. Змініть одне, інше зміниться.
Тім Холт

Відповіді:


9

Якщо я використовую архітектуру сервера / клієнта (з одним із гравців, який виступає і сервером, і клієнтом), і один з клієнтів породив групу дій, чи повинен він додати їх безпосередньо менеджеру, чи лише надіслати запит на сервер, що в свою чергу наказать кожному клієнту додати цю групу?

У вас є три варіанти в цьому випадку, два з яких ви вже згадали. Вибір того, який варіант ви будете використовувати, залежить від різноманітних факторів, які тільки ви можете бути найкращим суддею:

1: Клієнт створює групу дій, додає їх безпосередньо, а потім надсилає їх на сервер. Сервер приймає його без будь-яких перевірок

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

2: Клієнт відправляє запит на сервер, який, у свою чергу, транслює запит усім, включаючи клієнта, який створив запит.

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

3: Клієнт породжує групу дій, додає їх безпосередньо та потім відправляє на сервер. Сервер обробляє запит, здійснює власні перевірки дій, щоб переконатися, що вони не є незаконними, а потім передає перехід усім гравцям, включаючи клієнта.

Це сприймає найкращі показники як із 1, так і з 2, ціною трохи більших вимог пропускної здатності. Переваги - це миттєвий зворотний зв’язок з клієнтом щодо його власних кроків, і якщо кроки, які робить клієнт, є незаконними, сервер вживає відповідних дій (насильно виправляє клієнта).

Що з втратами пакетів тощо? Гра є детермінованою, але я думаю, що будь-яка невідповідність послідовності дій, виконаних у клієнта, може призвести до невідповідних станів світу. Як я захищаю від подібних проблем?

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

Одне з перших, що вам слід зробити - це відмітка часу всіма вашими рухами. Тоді ваша система повинна враховувати це і відповідно відтворювати рухи (можливо, сервер несе таку відповідальність, а потім відмовляє від незаконних кроків). При впровадженні цієї системи припустімо 0 затримок (тестуйте локально).

0 затримка, звичайно, не є хорошим припущенням навіть у локальних мережах, тому тепер, коли всі кроки поважають час, якому часу ви довіряєте? Ось тут і грає питання синхронізації часу. Багато статей / статей в Інтернеті допоможуть вам вирішити цю проблему.

Що робити, якщо я додати занадто багато дій одночасно, це не спричинить проблеми для з'єднання? Будь-який спосіб полегшити це?

Спочатку очевидні речі. Ніколи не надсилайте рядки або серіалізовані об'єкти. Не надсилайте повідомлення так швидко, як сама гра оновлюється. Надсилайте їх шматками (наприклад, 10 разів на секунду). Буде помилка (особливо помилка округлення), що серверу / клієнту потрібно буде виправляти помилки раз у раз (тому щосекунди сервер надсилає все [все = всі дані, що важливо, щоб у вашій грі все було правильним стан], який клієнт потім оснащується та / або враховує для виправлення свого стану).EDIT: Один з моїх колег згадав, що якщо ви використовуєте UDP [що вам слід, якщо у вас є багато даних], немає замовлення в мережі. Надсилання більше 10 пакетів в секунду збільшує зміни пакетів, які виходять з ладу. Я побачу, чи можу я цитувати цю претензію. Що стосується самих пакетів замовлень, ви можете їх відкинути або додати до черги повідомлень / переміщення вашої системи, яка призначена для обробки змін у минулому для виправлення теперішнього стану

У вас повинна бути система обміну повідомленнями, яка покладається на те, що займає дуже мало місця. Якщо вам потрібно надіслати щось, що може мати лише два значення, то надішліть лише один біт. Якщо ви знаєте, що у вас може бути лише 256 рухів, надішліть непідписаний знак. Існують різні трюкові трюки, щоб упакувати повідомлення якомога щільніше.

Ваша система обміну повідомленнями, швидше за все, використовує безліч перерахунків, які дозволять вам легко витягувати ходи з вхідного повідомлення (я б запропонував поглянути на RakNet та приклади, якщо ви не розумієте, що я тут маю на увазі).

Я впевнений, що ви вже знаєте, що не можете виправити проблеми, які виникають із затримкою висоти та втратою пакетів, але ви можете зробити її наслідки менш вираженими. Удачі!

EDIT: коментар Патріка Х'юза вище за посиланням робить цю відповідь неповною та конкретної для питання ОП у кращому випадку. Я б настійно пропонував замість цього прочитати пов'язані теми


Дуже дякую за детальну відповідь, що в значній мірі охоплює всі мої проблеми. Лише одне запитання щодо тієї частини, де ви говорите so every second, the server sends everything ... which the client then snaps to. Чи можу я просто надіслати одразу велику кількість такої інформації? Що таке розумний максимальний розмір?
Девід Гувейя

Доцільним максимумом буде число, яке не вводить відставання:) ... Я знаю, що ви не шукали такої відповіді, але я не хочу ставити номер, тому що я не знайомий з вашою грою.
Самаурса

Крім того, немає жодного жорсткого правила, що ви повинні надіслати один великий шматок, я просто наводив це як приклад.
Самаурса

@Samaursa - Я не погоджуюся з коментарем "Якщо ви використовуєте UDP [який ви повинні, якщо у вас є багато даних]" коментар. Оскільки вони новинки в мережевому програмуванні, TCP піклується про більшість важких речей для вас ціною, що вони набагато повільніше. У їхньому випадку я використовував би TCP, поки він не став вузьким місцем. У цей момент вони мали б краще розуміти, як їх додаток використовує мережу, тому реалізація матеріалів UDP буде простішою.
Кріс

@Chris: Я не погоджуюся :) ... рішення між використанням TCP та UDP - це як (іммо) рішення між вибором Python та C ++ для розробки. Теоретично це може звучати нормально, але на практиці буде важко просто переключитися (знову ж, це імо).
Самаурса
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.