Prediction

As a courtesy, please do not edit this while this message is displayed.
If this page has not been edited for at least several hours to a few days, please remove this template. This message is intended to help reduce edit conflicts; please remove it between editing sessions to allow others to edit the page.
The person who added this notice will be listed in its edit history should you wish to contact them.

This page either contains information that is only partially or incorrectly translated, or there isn't a translation yet.
If this page cannot be translated for some reason, or is left untranslated for an extended period of time after this notice is posted, the page should be requested to be deleted.
Also, please make sure the article complies with the alternate languages guide.
预测(Prediction)是客户端在无需等待服务器确认的情况下,预先模拟本地玩家操作效果的技术。实体的预测状态会与接收到的服务器指令进行比对,直到检测到匹配或失配。
在绝大多数情况下,服务器的确认会使客户端的预测得以延续,整个过程流畅得仿佛没有延迟。若出现失配(在预测代码正确编写的情况下较为罕见),客户端将回溯并使用修正后的数据重新模拟所有指令。根据错误严重程度,可能导致玩家位置、状态甚至世界状态出现明显卡顿。
预测与延迟补偿 紧密相关,但独立于插值 。预测仅存在于客户端。


实现方法
要实现实体预测:
- 实体必须能被本地玩家操控,否则预测无意义
- 需预测的功能必须在客户端和服务器端同时存在且完全一致,可通过共享代码 实现
- 实体需调用
SetPredictionEligible(true)
(建议在构造函数中执行) - 客户端需实现
bool ShouldPredict()
函数,用于检测本地玩家是否持有该实体等条件注意:武器还需在客户端实现
bool IsPredicted()
,始终返回true
- 所有需同步的变量必须进行网络传输 并注册到实体的预测表中
预测表
所有受玩家输入影响的客户端变量必须加入预测表,可选择三种注册方式:
FTYPEDESC_INSENDTABLE
- 网络传输变量。客户端预测值会与服务器值比对,若不同步将产生预测错误
FTYPEDESC_NOERRORCHECK
- 允许不同步的预测值(无论是否网络传输),不会触发预测错误
FTYPEDESC_PRIVATE
- 既不预测也不传输,但仍注册以便通过
cl_pdump
查看注意:此类变量在预测系统回溯时不会被保存/恢复。若预测函数修改其值,客户端在测试新指令时会持续累加该值
通过DEFINE_PRED_FIELD()
和DEFINE_PRED_FIELD_TOL()
宏实现上述功能。后者可为整型/浮点型变量设置误差容忍范围,常用于处理传输前的舍入(例如浮点数常被截断为1ms精度,此时可使用专为此设计的TD_MSECTOLERANCE
宏,否则需直接指定数值)。
注意:基于舍入值的推算需谨慎,传输值的微小差异可能导致计算结果大幅偏离!
DEFINE_PRED_TYPEDESCRIPTION
示例
#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
预测实体创建
标准预测系统仅维护现有实体状态,创建新实体需使用CreatePredictedEntityByName()
。该函数在客户端创建临时实体,待服务器实体到达后进行替换。务必通过shared code 调用。
CBaseEntity::CreatePredictedEntityByName( char* classname, char* class_file, int line, bool persist = false )
参数class_file
需填写类声明所在文件名(代码中称为module
),line
为行号(

),二者用于生成实体指纹。persist
使实体参与重预测检测。
若预测实体包含未传输数据,需在C_BaseEntity::OnPredictedEntityRemove()
中将数据复制到真实实体。
调试技巧
IsFirstTimePredicted()
通过prediction->IsFirstTimePredicted()
确保代码仅在首次预测时执行,避免在服务器更新校验时重复执行。需#include "prediction.h"
。
使用此函数生成随机数,其种子基于用户指令编号,保证客户端与服务器结果一致。
CDisablePredictionFiltering
[待完善]
抑制网络事件
非关键事件(如武器特效)可完全在客户端处理,抑制相关网络传输能有效降低带宽。
IPredictionSystem ::SuppressHostEvents()
专为此设计。调用时会暂停向指定玩家发送网络事件,传入NULL
恢复传输。例如:
if ( pPlayer->IsPredictingWeapons() )
IPredictionSystem::SuppressHostEvents( pPlayer );
pWeapon->CreateEffects();
IPredictionSystem::SuppressHostEvents( NULL );
注意:使用此方法后需编写代码让被抑制玩家的客户端自行生成特效。
示例
此处提供可输出预测信息到控制台的简易武器 。配合net_fakelag 200
(或更高)使用快速次要开火,可观察引擎如何处理多预测射击。

故障排查
若新增功能未遵循上述步骤,可能导致实体抖动或动画异常。可通过cl_predictionlist
和cl_pdump
调试。cl_pred_optimize
有时也有帮助。
假设cl_pdump
面板中某变量偶尔变红,说明客户端与服务器端该变量值不一致。常见原因:
- 客户端未运行部分服务器端代码(可能因
#ifdef GAME_DLL
或#ifndef CLIENT_DLL
隔离了相关代码) - 影响该变量的其他变量未加入数据表传输(客户端值始终错误)
- 未使用
DEFINE_PRED_FIELD_TOL
设置适当容差(例如4位精度的0.0-255.0浮点数需约17.0的容差)
排查预测问题通常需要追溯影响变量值的所有代码路径,检查相关变量同步情况。初期可能繁琐,但掌握方法后可快速定位。