Работа с Prediction системой

From Valve Developer Community
Jump to: navigation, search
English

Вступление

Система предсказания (prediction) движка Source спроектирована с целью уменьшить влияние задержек на динамику игры. Наиболее чувствительны к задержкам события, вызываемые нажатиями клавиш (во время чего команда отправляется на сервер и возвращается обратно), система предсказания Source имитирует эффект нажатия кнопки на клиенте немедленно. После этого, когда приходит ответ от сервера, клиент определяет, была ли корректной предполагаемая имитация.

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

Для более подробной информации можно прочитать Lag Compensation.

Подробности

Основные требования, которые требуется выполнить, чтобы энтити использовала систему предсказания:

  1. Одинаковые части кода энтити должны выполняться и на клиенте, и на сервере. Такой код относится к общему коду.
  2. Все переменные энтити которые меняются на сервере и клиенте должны передаваться при помощи таблиц данных.
  3. Эти переменные должны также быть добавлены в список называемый таблица предсказаний (prediction table).

Передача по сети через таблицы данных

Хорошим примером будет рассмотреть код оружия. Если у вас есть установленный исходный код, вы можете посмотреть файл src\game_shared\basecombatweapon_shared.cpp. Там, вы можете увидеть блоки #ifdef CLIENT_DLL и #ifdef GAME_DLL. Они определяют, какой код компилируется в клиентскую DLL, а какой в серверную. Приведенный ниже (сокращенный) код соответствует шагу 2 описанной выше последовательности (помещает переменные измененные общим кодом в таблицу данных).

BEGIN_NETWORK_TABLE(CBaseCombatWeapon, DT_BaseCombatWeapon)
#ifndef CLIENT_DLL
	SendPropInt( SENDINFO(m_iState ), 8, SPROP_UNSIGNED ),
	SendPropEHandle( SENDINFO(m_hOwner) ),
#else
	RecvPropInt( RECVINFO(m_iState )),
	RecvPropEHandle( RECVINFO(m_hOwner ) ),
#endif
END_NETWORK_TABLE()

Создание таблицы предсказаний

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

Примечание:Таблица предсказаний непременно нужна только в клиентской DLL, так вы можете ограничить код при помощи #ifdef CLIENT_DLL.
#ifdef CLIENT_DLL
BEGIN_PREDICTION_DATA( CBaseCombatWeapon )
	DEFINE_PRED_FIELD( m_nNextThinkTick, FIELD_INTEGER, FTYPEDESC_INSENDTABLE ),
	DEFINE_PRED_FIELD( m_hOwner, FIELD_EHANDLE, FTYPEDESC_INSENDTABLE ),
	DEFINE_PRED_FIELD_TOL( m_flNextPrimaryAttack, FIELD_FLOAT, FTYPEDESC_INSENDTABLE, TD_MSECTOLERANCE ),	
END_PREDICTION_DATA()
#endif

Консольные комманды

Если вы добавляете функциональность к оружию и забываете выполнить шаги описанные выше, работа энтити может сопровождаться рывками и странностями с анимацией. Вы можете использовать cl_predictionlist и cl_pdump , чтобы устранить эти проблемы.

Подавление сетевых данных

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

Интерфейс IPredictionSystem предназначен для этого. Когда IPredictionSystem::SuppressHostEvents( pPlayer ); вызван, все сетевые данные к этому пользователю останавливаются. Отправка NULL повторно остановит подавление. Для примера:

if ( pPlayer->IsPredictingWeapons() )
IPredictionSystem::SuppressHostEvents( pPlayer );

pWeapon->CreateEffects();

IPredictionSystem::SuppressHostEvents( NULL );


Решение проблем

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

  1. Клиент не выполняет тот же код что выполняет сервер. Это может быть случай, если участок кода помещен в секцию ограниченную #ifdef GAME_DLL или #ifndef CLIENT_DLL вокруг кода который влияет на переменную отмеченную красным цветом.
  2. Другая переменная которая влияет на данное значение становящееся красным не передается через таблицу данных. В таком случае на клиенте, значение этой переменной всегда неверное (так как сервер никогда не передает его).
  3. Также возможно не было добавлено соответствующее отклонение при помощи макроса DEFINE_PRED_FIELD_TOL. Например, если передается значение с плавающей запятой в диапазоне от 0.0 до 255.0, и было задано только 4 бита точности, тогда требуется отклонение около 17.0, иначе система предсказания считает значение переменной неверным из-за того что значение сжимается до 4 бит перед отправкой клиенту.

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