Коротко о «неткоде»: как работают сетевые взаимодействия в играх

Основные принципы устройства онлайн-игр и «почему он попал в меня, хотя я уже был за углом».

Embedded Image

Некоторые современные многопользовательские игры страдают от плохого сетевого кода — урон по противнику может пройти гораздо позже удара или выстрела, а иногда, независимо от скорости соединения, он даже мешает нормально передвигаться по игровому миру.

В этом тексте собрали для вас основные факты о том, как работают сетевые игры и как разработчики оптимизируют их «общение» с интернетом — от основных способов взаимодействия с сервером до жанровых особенностей сетевых проектов.

Сетевой код в играх целиком завязан на «диалоге» с сервером. Есть несколько основных моделей таких взаимодействий: выделенный сервер, listen server и одноранговая сеть (peer-to-peer).

Выделенный сервер находится на специально отведённой ферме, и все игроки подключаются к нему напрямую. Из-за того, что большая часть системы находится в одном месте, разработчикам проще следить за использованием читов и сетевыми неполадками. Обычно такие серверы дорого стоят, так что купить их могут разве что крупные компании. Ещё один вариант — арендовать их, но и это стоит недёшево.

Для комфортной игры приходится обеспечивать несколько региональных ферм, расположенных по всему земному шару. Такая система используется, например, в Fortnite и PUBG.

PlayerUnknown’s BattleGrounds

Listen server — ещё одна популярная сетевая модель. Условным «сервером» в ней становится один из игроков, к которому подключаются все остальные. С одной стороны, такая система решает проблему с глобальной недоступностью игры в случае отключения серверов, с другой — любая неполадка в соединении «хоста», то есть «игрока-сервера», приводит к проблемам у всех остальных игроков. К тому же, в такой системе сложнее контролировать «честность» игроков и блокировать читы. Кстати, эта сеть очень популярна в кооперативных играх.

И в том, и в другом случае сервер — это не какое-то отдельное приложение, а клиент игры, у которого есть приоритет в определении того, что на самом деле произошло. Именно сервер верифицирует перемещения, регистрацию попаданий и прочие моменты. При этом на остальных клиентах происходит симуляция с определёнными предсказаниями.

Например, игроки будут продолжать двигаться со скоростью и направлением, которые последними получили с сервера. При небольшом пинге и без проблем с производительностью на клиентской машине движение рассчитывается успешно, и всё выглядит достоверно.

Left 4 Dead

Наконец, в одноранговой сети (peer-to-peer) все игроки соединяются напрямую друг с другом. Применяют её только в том случае, когда правила игры предсказуемы и можно достоверно и без ошибок просчитать статус каждого объекта у всех игроков.

Иногда разработчики пытаются сэкономить и реализуют P2P в играх, где эта система не совсем подходит — как, например, поначалу было в Rainbow Six Siege и For Honor. В них много «рандома», да и геймплей требует чёткого расчёта многих показателей — а система peer-to-peer с этим справлялась не всегда.

Обычно модель выбирают в зависимости от жанра игры, хотя большие студии чаще всего используют выделенные серверы. Инди-команды от них отказываются из-за сложности и недостатка ресурсов для аренды и обслуживания.

Методы оптимизации игрового кода во многом схожи с методами оптимизации ПО. Всё сводится к разумному использованию доступной памяти — лучше не засорять её бесполезными данными и использовать легковесные переменные.

Прежде всего, в играх стараются избегать непосредственных вычислений. По словам программиста Кайла Слока-Фрей, если в игре часто используется одна и та же числовая константа, то лучше сохранить её вместе с другими подобными значениями в отдельной таблице, чем рассчитывать заново каждый раз при необходимости. Обращение к таблице в данном случае будет менее ресурсозатратно, даже если она состоит из большого числа величин.

Другой частый способ оптимизации — уменьшение количества незначительных операций вне экрана. К примеру, не нужно просчитывать движение листьев на деревьях, которые игрок даже не видит — хотя условные выстрелы вдалеке регистрировать всё равно необходимо. Этот принцип работает как в многопользовательских, так и в одиночных играх.

Rainbow Six Siege

Похожее правило относится и к вызову функций в каждом кадре. Вместо того, чтобы проверять состояние объекта несколько десятков раз в секунду, можно делать это лишь при выполнении определённых условий. К примеру, нет смысла каждое мгновение проверять, равно ли здоровье персонажа нулю — это требуется только тогда, когда он получает урон.

Но особенно важно в многопользовательских играх оптимизировать сетевой трафик. По словам Владимира Алямкина из Mail.ru, первым делом нужно ограничить количество запросов в секунду, которые делают игровые объекты для синхронизации с сервером.

Для хардкорного квейкообразного шутера всё, что касается персонажа — его движение и оружие, — должно синхронизироваться с максимальной частотой. В некоем «усредненном» случае — аркады, MOBA, мобильные игрушки, а также «медленные шутеры» а-ля танки и прочие — частота сетевых обновлений может и должна быть гораздо ниже. В Armored Warfare: Assault мы используем частоту обновления состояния танка 10 раз в секунду. Я также знаю проекты, которые работают исходя из частоты сетевых обновлений главного персонажа 6-8 раз в секунду.

Embedded Image

технический лид в Pushkin Studio (MY. GAMES)

Источник цитаты: habr.com

Armored Warfare: Assault

Если же в игре есть сложный объект, обновление данных о котором с нуля занимает очень много времени, лучше делать запросы о нём поэтапно, так как полный апдейт не позволит получать информацию с сервера другим объектам. Например, вместо того, чтобы подгружать танк целиком, сначала клиент подгружает его условный номер и скорость, а затем уже 3D-модель. Так в промежутках между подгрузкой разных «частей» танка можно получать с сервера данные о других объектах в сцене.

Оптимизировать сетевой код можно также отказом от ненужных запросов. Некоторые типы данных необязательно «реплецировать» — то есть копировать с сервера — слишком точно, достаточно сделать это с небольшой погрешностью, чтобы не нагружать сервер.

А точным обновлением некоторых объектов вообще можно пренебречь: иногда положение разрушаемых деревьев или зданий некритично для игры, поэтому можно не запрашивать у сервера информацию о них, а оставлять их целиком на стороне каждого игрока, если они не влияют на геймплей. Например, упавшее дерево вряд ли остановит танк, так что его положение не нужно запрашивать каждому из игроков.

У каждого жанра свои особенности в работе и оптимизации сетевого кода, но так или иначе они завязаны на взаимодействии игры с сервером. Например, в многопользовательских шутерах часто возникает проблема «преимущества нападающего», когда человек, который выглядывает из-за угла, находится в заведомо более выгодном положении, чем тот, который выходит из-за угла на другом конце карты.

VALORANT

Из-за особенностей передачи данных между сервером и игроком появляется небольшое «окно» между выстрелом нападающего игрока и реакцией обороняющегося. Смерть второго засчитывается быстрее, чем он начинает стрелять, поэтому его выстрелы уже не регистрируются. Особенно эта проблема заметна в играх с низким ТТК, или временем на убийство, которое мы уже разбирали подробно.

Для того, чтобы исправить эту проблему, в Riot Games для Valorant разработали собственную сеть Riot Direct. Она позволила снизить время обращения к серверу и убрать задержки в соединении, благодаря чему разница в реакциях игроков стала не столь значительной. Эта сеть решила одну из задач разработчиков — ведь Valorant задумывалась как шутер, в который смогут одинаково комфортно играть люди с разной скоростью интернета.

Разработчики используют одноядерные машины в качестве серверов. При этом на одном ядре может быть запущено до трёх серверов. Так что далеко не всегда сервеыа являются супермощными машинами. Другое дело, что их может понадобиться весьма много для хостинга большого количества сессий, а это тоже деньги.

Embedded Image

геймплей-программист, преподаватель курса по программированию XYZ School

Что же до привычной ситуации с «опоздавшим уроном», её исправляет так называемая компенсация отставания.

Она нужна для того, чтобы предотвратить отставание в обработке данных, возникшее из-за разницы в соединениях игроков. Без компенсации игрок с более быстрым интернетом может попасть в другого, даже когда тот зашёл за угол — ведь клиент первого игрока банально не успел получить информацию о том, что второй уже скрылся.

Компенсация же эту особенность учитывает: благодаря ей игрок с более быстрым интернетом стреляет не в предыдущее место противника, а в его предполагаемое нынешнее — которое высчитывается на основе последней зафиксированной скорости перемещения врага.

Важно понимать, что компенсация происходит в клиенте игрока — поэтому высчитанное положение может отличаться от реального положения вещей, которое зафиксировал сервер.

Другой жанровый пример оптимизации кода — Satisfactory, симулятора строительства инопланетных заводов. Перед разработчиками стояла задача уменьшить количество информации, которую нужно посылать на сервер — в игре в какой-то момент одновременно начинают работать огромное количество конвейерных лент с тысячами единиц объектов грузов. Поэтому им пришлось искать необычные способы оптимизации.

Satisfactory

Прежде всего, они отказались от отправки данных о тех сущностях, которые не видит игрок, вроде закрытого инвентаря. Также серверу реже посылалась информация об объектах, состояние которых не менялось со временем. Например, контейнеры на конвейерах двигаются предсказуемо и с одной скоростью, так что считывать информацию о каждом из них для отправки на сервер не требуется.

Satisfactory

Совсем другую систему обычно применяют в файтингах — при совместной игре в них нужно учитывать, что ваше устройство будет обрабатывать ваши удары быстрее, чем удары соперника. При использовании отложенного сетевого кода происходит задержка между вашим нажатием кнопки и ударом персонажа, так что, в теории, ваши с соперником атаки считаются примерно в одно и то же время.

Например, если вы нажали кнопку на геймпаде на 3 кадре секунды, то игра отложит исполнение атаки до 6 кадра, чтобы команда соперника совершить удар «добралась» до игры. Основное преимущество такого подхода в том, что он довольно прост в реализации и позволяет не прорабатывать боевую систему онлайн-режимов отдельно от одиночных, потому что поведение персонажей в них не отличается. Отложенный сетевой код использует большинство файтингов — от Mortal Kombat 11 и Killer Instinct до более нишевых Skullgirls и Rocket Rumble.

Комментарии: 0