Source Multiplayer Networkingru - Valve Developer Community
Многопользовательские игры на движке Source употребляют архитектуру Клиент/Сервер. Обычно Сервер — это выделенная машина, на которой запущена игра и которая диктует симуляцию игрового мира, правила игры и результаты обработки действий игрока. Клиент — это компьютер игрока, присоединенный к игровому серверу. Клиент и сервер разговаривают меж собой методом нередкой посылки маленьких пакетов с данными (обычно 20-30 пакетов за секунду). Клиент получает текущее состояние игрового мира от сервера и на базе этих данных генерирует картину и звук. Клиент также получает данные с устройств ввода (клавиатура, мышь, микрофон и т.д.) и посылает эти данные на сервер для следующей обработки. Клиенты разговаривают только с сервером, но не меж собой (в отличие от приложений с архитектурой peer-to-peer). В отличие от однопользовательских игр, многопользовательским требуется решать широкий диапазон заморочек, связанных с общением на базе передачи пакетов данных.
В силу того, что пропускная способность сети ограничена, сервер не может посылать пакет с обновлением всем клиентам всякий раз, когда в игровом мире происходит изменение. Заместо этого, сервер делает мгновенные снимки состояния игрового мира через равные промежутки времени и передает эти снимки клиентам. На доставку пакета с данными от сервера к клиенту и назад требуется определенное время (ping). Это значит, что время на клиенте всегда малость отстает от времени сервера. Более того, команды ввода с клиента тоже должны дойти до сервера, так что сервер тоже обрабатывает пользовательские деяния с задержкой. В добавок, время прохождения пакета у каждого клиента отличается зависимо от типа соединения, фонового трафика и частоты обновлений. Эти различия во времени меж сервером и клиентом порождают разные логические трудности, которые становятся еще серьезнее при возрастании пинга. В боевиках с резвым геймплеем даже миллисекундные задержки могут вызвать чувство лага и существенно затруднить взаимодействие и попадание по передвигающимся объектам. Кроме ограничений, накладываемых пропускной способностью и пингом, трудности может вызывать к тому же утрата пакетов.

Движок Source употребляет несколько разных техник для того, чтоб совладать с этими неуввязками либо по последней мере сделать их наименее видимыми игроку. Эти техники включают сжатие данных, интерполяцию, пророчество и лаго-компенсацию. Все эти техники тесновато взаимосвязаны и конфигурации в одной системе могут серьезно воздействовать на другие. Этот документ обрисовывает общею функциональность этих систем и механизмы их взаимодействия.
Базы сетевого кода
Сервер симулирует игровой мир в дискретные промежутки времени, нареченные «тик» (tick). По дефлоту употребляется 66 тиков за секунду, но моды могут использовать свою свою частоту тиков. К примеру в Counter-Strike: Source употребляется частота 66 тиков за секунду. Во время каждого тика сервер обрабатывает пользовательские команды, симулирует физику, инспектирует правила игры и обновляет состояние объектов игрового мира. После окончания симуляции тика, сервер определяет каким клиентам требуется обновление и делает снимок состояния мира, если это нужно. Более высочайшая частота тиков наращивает точность симуляции, но просит огромных ресурсов микропроцессора и пропускной возможности как на сервере, так и на клиенте. Админ сервера может установить значение частоты тиков при помощи параметра -tickrate в строке пуска сервера, но схожее изменение не рекомендуется, потому что работа мода может быть нарушена при установке необычного значения частоты тиков.
Пропускная способность клиента обычно ограничена. В худших случаях, игрок с модемным соединением может принимать менее 5-7 Кб/сек. Если б сервер попробовал отсылать ему обновления с большей частотой, то утрата пакетов стала бы неминуемой. Потому, клиент должен сказать серверу о доступной входящей пропускной возможности при помощи консольной переменной rate (в б за секунду). Это более принципиальная сетевая переменная для клиента и она должна быть выставлена правильна для заслуги хорошей производительности. Клиент может также добиваться определенную частоту обновлений изменяя значение переменной cl_updaterate (значение по дефлоту: 20), но сервер никогда не отправит обновлений больше, чем способен создавать тиков либо же чем установленное клиентом значение rate. К тому же, админ сервера может ограничивать количество посылаемой инфы переменными sv_minrate и sv_maxrate, а частоту обновлений при помощи серверных переменных sv_minupdaterate и sv_maxupdaterate.
Клиент делает пользовательские команды опрашивая устройства ввода с той же частотой, с которой работает сервер. Пользовательская команда — это просто говоря, снимок текущего состояния клавиатуры и мыши. Но заместо отправки на сервер нового пакета на каждую пользовательскую команду, клиент шлет обновления с определенной частотой за секунду (обычно 30). Это означает, что две либо более команд передаются в каждом пакете. Игрок может прирастить частоту отправляемых пакетов при помощи команды cl_cmdrate. Это сделает лучше то, как реагирует клиент, но в то же время востребует большей пропускной возможности.
Для уменьшения нагрузки на сеть употребляется так именуемая дельта-компрессия. Это означает, что сервер отправляет не полный снимок, а разницу меж текущим и последним подтвержденным клиентом снимками (дельта-снимок). С каждым пакетом, передаваемым меж сервером и клиентом, передаются данные о доказательстве приема, чтоб выслеживать непрерывность потока. Обычно полные снимки состояния мира передаются исключительно в начале игры либо в случае, когда клиент мучается от сильной утраты пакетов в течении нескольких секунд. Клиент может запросить полное обновление вручную при помощи команды cl_fullupdate.
Время реакции, другими словами время, проходящее меж совершением юзером определенного деяния и отражением этого деяния в игровом мире, находится в зависимости от многих причин, включая загрузку микропроцессора на клиенте и сервере, частоте тиков, скорости передачи данных, настройках обновлений, но больше всего от времени прохождения пакета по сети. Время, проходящее меж отправкой клиентом пользовательской команды и реакцией на нее мира вокруг нас, именуется задержкой либо пингом (latency либо ping). Маленький пинг является суровым преимуществом в онлайновой многопользовательской игре. Такие вещи, как пророчество и лаго-компенсация ориентированы на то, чтоб минимизировать это преимущество и дать возможность игрокам с неспешным соединением играть на равных с остальными. Настройка сетевых переменных также помогает сделать лучше производительность при условии достаточной производительности микропроцессора и пропускной возможности. Мы советуем придерживаться стандартных значений, потому что неправильные опции могут привести с нехорошим эффектам заместо полезности.
Интерполяция
По дефлоту клиент получает 20 снимков игрового мира за секунду. Если б объекты мира вокруг нас отрисовывались лишь на позициях, продиктованных сервером, любые передвигающиеся предметы и анимация выглядели бы прерывисто. Потерянные пакеты также порождали бы приметные зрительные трудности. Для того, чтоб избежать этого, весь рендеринг происходит в прошедшем так, что клиент безпрерывно вычисляет движение объектов меж 2-мя последними принятыми снимками. Эта техника именуется интерполяцией на стороне клиента и включена по дефлоту командой cl_interpolate 1. При 20 обновлениях за секунду, новый снимок доставляется клиенту в среднем один раз в 50 миллисекунд. Если б рендеринг на клиенте происходил с задержкой в 50 миллисекунд, клиент мог бы всегда интерполировать меж текущим и последним снимками. В Source употребляется интерполяция с задержкой в 100-милисекунд (cl_interp 0.1). Благодаря этому, даже если один снимок потерян, у клиента всегда есть два корректных обновления для интерполяции. Иллюстрация, показывает время прибытия снимков игрового мира.

Последний снимок был получен клиентом на тике 344 либо на позиции 10.30 секунд. Клиент продолжает отсчитывать время основываясь на этом снимке. Новый видео кадр же рендерится во времени, равном текущему времени клиента минус задержка интерполяции (10.32 — 0.1). В нашем примере это время составит 10.22 секунды и все объекты и анимация интерполируются меж корректными снимками 340 и 342.
Беря во внимание, что задержка интерполяции у нас составляет 100 миллисекунд, мы получили бы реальное отражение мира даже в случае, если б снимок 342 был недоступен из-за утраты пакетов. В данном случае интерполяция использовала бы снимки 340 и 344. При потере более чем 1-го снимка попорядку интерполяция будет работать неправильно из-за отсутствия достаточного количества снимков в буфере. В данном случае употребляется экстраполяция (cl_extrapolate 1) — обычное линейное пророчество позиций объектов на базе истории их поведения ранее. Экстраполяция совершается только для утраты пакетов до 0.25 секунды (cl_extrapolate_amount), потому что далее ошибки пророчества становятся очень значительны.
Интерполяция вызывает непрерывную задержку отображения в 100 миллисекунд даже, если вы играете на невыделенном сервере (сервер и клиент размещены на одном и том же компьютере и сервер запущен прямо из игры). Так что если вы включите команду sv_showhitboxes, то хитбоксы игроков будут отрисовываться по времени сервера, другими словами будут на 100 миллисекунд опережать модель игрока. Это но не значит, что вам нужно целиться впереди противника, потому что лаго-компенсация на стороне сервер знает об интерполяции клиента и учитывает ее при подсчете попаданий. Если вы отключите интерполяцию на невыделенном сервере (cl_interpolate 0), то хитбоксы опять совпадут с моделью игрока, но анимация и движение станут дерганными и прерывающимися.
Пророчество ввода
Давайте представим, что пинг игрока составляет 100 миллисекунд и игрок начинает движение вперед. Информация о нажатии кнопки +FORWARD сохраняется в пользовательской команде и отчаливает на сервер. Код обработки движения обрабатывает команду и игрок начинает двигаться вперед в игровом мире. Это состояние игрового мира передается всем клиентам совместно со последующим снимком. В итоге игрок увидит собственное движение с задержкой 100 миллисекунд с момента как он надавил на кнопку. Эта задержка накладывается на любые деяния игрока, будь то движение, стрельба либо что или еще, и ситуация становится еще ужаснее при увеличении пинга.
Задержка меж нажатием кнопки и его зрительным отображением делает странноватое, противоестественное чувство, в итоге которого очень трудно двигаться и стрелять точно. Для устранения этой задержки и обеспечения игроку способности чувствовать конфигурации одномоментно употребляется пророчество ввода (cl_predict 1). Заместо того, чтоб дожидаться от сервера инфы об изменении своей позиции, клиент предвещает результаты собственных пользовательских команд при помощи точно того же кода, который употребляет для этой цели сервер. После окончания пророчества, игрок одномоментно перемещается на новейшую позицию, хотя сервер все еще «лицезреет» его на древнем месте.
Через 100 миллисекунд клиент получит от сервера снимок игрового мира, содержащий конфигурации, основанные на пользовательской команде, предсказанной ранее. Клиент ассоциирует данные сервера с плодами пророчества. Если они различаются, происходит ошибка пророчества. Это значит, что у клиента не было корректной инфы об окружающих объектах, чтоб произвести корректное пророчество, либо же что пользовательская команда не была доставлена в итоге утраты пакетов. В этом случает клиент корректирует позицию игрока, потому что информация сервера является решающей по отношению к клиенту. Если включена команда cl_showerror 1, клиент может созидать ошибки пророчества. Исправление ошибок пророчества может быть очень приметным и может привести к скачку изображения. Для смягчения этого эффекта исправление ошибок пророчества происходит не одномоментно, а растягивается в течение недлинного времени (cl_smoothtime). Смягчение ошибок пророчества может быть выключено командой cl_smooth 0.
Пророчество поведения объектов может работать только если клиент знает те же правила и состояния объектов, что и сервер. Как правило это не так, т.к. серверу известна полная информация о всех клиентах и объектах, в то время как отдельный клиент лицезреет только ту часть игрового мира, которая нужна для корректного рендеринга. В итоге, пророчество ввода действует только для самого игрока и орудия, которое он употребляет. Четкое пророчество других игроков и интерактивных объектов на клиенте нереально.
Лаго-компенсация
Давайте представим, что игрок стреляет в цель во время 10.5. Информация о выстреле упаковывается в пользовательскую команду и отчаливает на сервер. Пока пакет находится в пути, сервер продолжает симулировать игровой мир и мишень могла двинуться на другую позицию. Пользовательская команда прибывает на сервер в момент 10.6 и сервер не засчитывает попадание, хотя игрок целился точно в цель. Эта неувязка исправляется серверной лаго-компенсацией (sv_unlag 1).
Система лаго-компенсации хранит историю всех недавнешних позиций игроков на промежутке времени приблизительно в секунду (это можно поменять переменной sv_maxunlag). При выполнении пользовательской команды, сервер подсчитывает время, когда она была сотворена по формуле:
Время выполнения команды = Текущее время сервера — Пинг клиента — Задержка интерполяции клиента
Далее сервер перемещает всех игроков на позиции, которые они занимали в момент выполнения команды. Пользовательская команда исполняется и попадание засчитывается. После обработки пользовательской команды игроки ворачиваются назад на свои позиции. На невыделенном сервере вы сможете использовать команду sv_showimpacts 1, чтоб созидать разницу в пользовательских и серверных хитбоксах:

Этот снимок экрана был снят на невыделенном сервере с задержкой 200 миллисекунд (используя команду net_fakelag) сходу после доказательства попадания сервером. Красноватый хитбокс показывает позицию цели на клиенте 100 миллисекунд вспять. С этого момент цель продолжила движение на лево пока пользовательская команда доставлялась на сервер. После получения пользовательской команды сервер вернул позицию цели (голубий хитбокс) на базе подсчитанного времени выполнения команды. Сервер отследил выстрел и подтвердил попадание (клиент лицезреет всплеск крови). Хитбоксы на сервере и на клиенте не стопроцентно совпадают в итоге маленьких ошибок при подсчете времени. Но для быстродвижущихся объектов даже задержка в несколько миллисекунд может привести к смещению в несколько см. Регистрация попаданий в многопользовательском режиме не попиксельно точна и имеет свои ограничения зависимо от скорости объекта и установленного значения частоты тиков. Повышение частоты тиком улучшает регистрацию попаданий, но в то же время просит большей нагрузки на микропроцессор и большей пропускной возможности как на сервере, так и на клиенте.
Появляется закономерный вопрос: почему регистрация попаданий на сервере так сложна? Для чего разбираться с откатами в прошедшее для выяснения позиций игроков и неуввязками с точностью если все это можно обработать на клиенте без заморочек и с точностью до пикселя. Клиент же мог бы просто посылать серверу сообщение о попадании. Этого мы позволить не можем просто поэтому, что сервер не может доверять клиенту в настолько ответственных решениях. Даже если сам клиент чист и защищен VAC (Valve Anti-Cheat), пакет с данными можно видоизменять при помощи 3-й машины по пути до сервера. Подобные «cheat прокси» могли бы вставлять сообщения о попадании в пакет минуя защиту VAC.
Задержки сети и лаго-компенсация порождают парадоксы, выглядящие нелогично в реальном мире. К примеру, в вас может попасть противник, которого вы уже даже не сможете созидать, так как вы уже скрылись в укрытии. Это случается поэтому, что сервер передвинул вас в прошедшее на позицию, где вы были еще видимы. Подобные несоответствия не могут быть решены из-за относительно низкой скорости передачи пакетов. В реальном мире вы не замечаете схожих заморочек поэтому, что свет («пакет с информацией») путешествует настолько стремительно, что все вокруг лицезреют мир таким же, как и вы на этот момент.
Net Graph
В Source существует несколько инструментов, позволяющих проверить скорость и качество соединения клиента. Самый пользующийся популярностью из их — это net graph, который можно включить командой net_graph 2. Входящие пакеты отражаются в виде маленьких линий, передвигающихся справа влево. Высота каждой полосы отражает размер пакета. Если меж линиями появляется разрыв, это означает что пакеты пришли в неправильном порядке либо были потеряны. Цвет линий отражает содержащуюся в пакете информацию.
Под графической частью 1-ая строчка докладывает количество отрисовываемых кадров за секунду, среднюю задержку и текущее значение cl_updaterate. 2-ая строчка указывает размер последнего пакета (снимка) в б, среднюю применяемую пропускную способность и количество приобретенных пакетов за секунду. В третьей строке показываются эти же данные, но для исходящих пакетов (пользовательских команд).