Оптимизация сетевого протокола Клиент-Сервер

Материал из CSM Wiki
Перейти к навигации Перейти к поиску

Обзор

Проектирование многопользовательских игр для Интернет является сложным процессом. Наличие качественного on-line геймплея в вашем экшене, является неотъемлемой частью успеха и долголетия продукта. Кроме того, в компьютерной индустри хорошо известна необходимость разработчиков в поддержке большого количества клиентских конфигураций. Зачастую пользователи используют не слишком современное оборудование, это-же справедливо и для сетевой конфигурации.

Хотя внедрение широкополосного Интернета стало панацеей от всех текущих проблем для многих on-line игр, оказалось не простым решением для разработчиков позволяющим игнорировать задержку и остальные сетевые факторы в играх. Пройдет много времени прежде чем широкополосный интернет будет принят в США и на много больше в остальном мире. Кроме того, есть множество "плохих" сетей, в которых пользователь порой имеет высокую пропускную способность, зачастую и нет, но наряду с этим значительную задержку и потерю пакетов в соединении.

В ходе обсуждения будет дано некоторое представление о том как работает клиент-серверная архитектура во многих on-line играх. Кроме того, в ходе обсуждения будет показано, как прогнозирующее моделирование может быть использовано для маскировки эффекта задержки. А так-же специфику механизма лаг-компенсации за счёт качества соединения.

Базовая архитектура Клиент/сервер

Большинство современных игр в сети — это модифицированный клиент-сервер игровой движок. Игры такие как Half-Life включая её различные модификации Counter-Strike или Team Fortress Classic, работают по такой-же системе что и игры основанные на игровых движках Quake3 и Unreal Turnament. В этих играх существует один главный сервер который отвечает за работу основной логики игры. К нему подключаются один и более "немых" клиентов. Эти клиенты, изначально, представляют из себя сетевое соединение между пользователем и сервером для отправки снимка ввода и его исполнения на сервере. Сервер исполняет полученные команды, передвигает объекты, просчитает движение и отправит обратно клиенту список этих объектов для рендеринга изображения. Конечно реалтаймовая система состоит из большего количества объектов, но простейшее разложение полезно для понимания прогнозирования и лаг-компенсации.

Имея это ввиду типичная клиент/сервер архитектура выглядит примерно таким образом:

Lagcomp1.png

Далее показано всё что нужно что-бы начать обмен сообщениями и координацию между клиентом и сервером. Цикл снятия снимка клиента выглядит следующим образом:

  1. Запускается таймер снимка
  2. Снимок ввода (мыши, клавиатуры, джойстика)
  3. Формирование пакета и посылает команду отправки используя смоделированное время (из таймера?)
  4. Чтение пакета полученное от сервера из сети
  5. Обработка пакета для визуализации объектов и их положения
  6. Рендеринг сцены
  7. Заканчивается таймер снимка
  8. Время конца минус время старта — смоделированное время для следующего кадра (фрейма)

Каждый раз как клиент проходит через этот цикл, "время фрейма" служит для определения смоделированного времени (framerate) на следующий фрейм. Если ваша частота кадров является постоянной тогда значение времени фрейма будет правильным. В противном случае значение будет вычислено не верно, пока вы не сможете точно определить сколько раз нужно использовать цикл для следующего фрейма перед его запуском...

Сервер имеет в чём-то схожий цикл:

  1. Запускается таймер снимка
  2. Чтение сообщения клиента из сети
  3. Исполнение сообщения ввода клиента
  4. Имитируются объекты управляемые сервером с помощью смоделированного времени из предыдущего полного прохода цикла
  5. Для каждого подключенного пользователя формируется пакет о визуальном состоянии объекта/мира и отправляется клиенту
  6. Заканчивается таймер снимка
  7. Время конца минус время старта — смоделированное время для следующего фрейма

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

Содержимое сообщения пользователя

В играх основанных на Half-Life движке, сообщение пользователя имеет очень простой формат заключённый в структуре данных всего в нескольких очень важных полях:

typedef struct usercmd_s
{
	// Интерполяция времени клиентом
	short		lerp_msec;   
	// Продолжительность команды в микросекундах
	byte		msec;      
	// Команда изменения угла обзора
	vec3_t	viewangles;   
	// Скорость движения назад
	float		forwardmove;  
	// Боковая скорость
	float		sidemove;    
	// Скорость движения вперёд
	float		upmove;   
	// Нажатие клавиши
	unsigned short buttons; 
	//
	// Остальные поля опущены...
	//
} usercmd_t;

Главное значение здесь имеют поля msec, viewangles, forward, side, upmove и buttons. msec соответствует количеству миллисекунд