|
_Host_RunFrame()是一个两百多行的特别婆婆妈妈的函数。
感觉象是一大堆稀稀拉拉牵牵扯扯的脂肪下水中夹杂了几丝精肉。
这些精肉估计是这几个Host_RunFrame_XXX()
及Host_XXX()函数,还有Cbuf_Execute()。
其它的罗嗦代码是处理时间,有time 及tick好几个变量,
这些时间有什么意义尚需探查。
先把这堆下水扯成几块,一块一块地理比较好。
函数的下半部分是一个" rofile scope"块,
"Profile scope"是什么意思?
看注释表明这是setjmp()保护的代码块。
setjmp其实就是一个异常保护机制,
在C++里应该使用异常,而不是setjmp。
{
// Profile scope, protect from setjmp() problems
...
} // Profile scope, protect from setjmp() problems
上半部分没有什么实质,拉扯开来如下:
1 堆检查,仅供调试,无用。
2 ResetTimeMeasurements(),
// Called once per frame to allow any necessary measurements to latch
可能是调试用代码,具体不详。
粗看代码是对CMeasureSection链进行重置,排序。
CMeasureSection主要是对Section做时间累计。
暂时忽略它。
3 setjmp() 保护下半部分代码,忽略。
4 Host_FilterTime(time)
输入帧间隔时间,记录于host_frametime全局变量,
并累加到realtime全局变量。以下用到。
5 _Host_SetGlobalTime()
设置g_ServerGlobalVariables中与时间相关的几个成员变量。
使用到了上述host_frametime,realtime全局变量。
如果合并到上面Host_FilterTime()中完成,代码会清晰点。
各种时间值的详细含义可见CGlobalVarsBase中的定义。
g_ServerGlobalVariables设置暂时可忽略。
(看函数代码是否可以认为"SWDS"宏定义为"Sxxx Wxxx Dedicated Server"?)
6 计算时间与滴嗒值几个局部变量值,
这些变量下面的"Profile scope"块中用到。
prevremainder
host_remainder
extramousetime
numticks
一个滴嗒(tick)为15ms,
一个帧间隔时间就分成数个tick, 和一个余数(remainder)。
余数时间就留到下一次帧动作时处理。
还有一个鼠标滞后时间,对应于余数,
但鼠标不能滞后到下一次再处理。
这些变量的意义和计算都挺简单,但实现的代码复杂混乱,
谁愿意改改?
下面半部分"Profile scope"块,
进入时调用一下 VPROF( "_Host_RunFrame" );
应该是记录一个Profile信息,
那么整个"Profile scope"就是需要Profile跟踪的代码块之意了。
g_HostTimes.StartFrameSegment()
与g_HostTimes.EndFrameSegment()
中间夹一个操作就是时间概要(time profile)跟踪了。
Host_Speeds()调用g_HostTimes.MarkFrame()就打印出所有时间概要信息,
显示各操作所花费的时间,用于代码优化。
_Host_RunFrame_XXX()函数里都套用这一模式。
与VPROF()一样,概要(profile)操作仅用于调试,一概忽略。
(Demo有关的也忽略?知道demo是怎么回事吗?)
整个"Profile scope"块分为数个部分:
1Cbuf_Execute()执行控制台命令。
Cbuf就是Command Buffer的意思了,
所有命令以字符串方式存放于命令缓冲区,
Cbuf_Execute()就是一个一个取出命令并执行。
有空就去看看这个函数吧。这样你就知道你敲入的命令是怎么执行的了。
2 tick循环
前面已经计算出来这个帧间隔需要几个tick, 现在就循环执行几遍,
为什么这些操作需要每个滴嗒执行一次,而不是每个帧执行一次?待我看来。
2.1 CL_CheckClientState()检查客户端与服务器的连接。
断线重连?用不着tick循环。
2.2 _Host_RunFrame_Input( prevremainder )
处理客户端的输入。也没发现tick循环的理由。
即使真要tick循环,也应该循环Host_RunFrame_Input(0),
prevremainder应该在循环前处理。
如果tick不需处理,那么remainder也就不需要了,是否可以一齐简化?
2.3 _Host_RunFrame_Server(finaltick),
与_Host_RunFrame_Client(finaltick)
分别处理服务器与客户端的所有事务(关键动作,慢慢再看,仔细阅读)。
其实从这些函数的名字中就可以认定只需每帧执行一次就行了,
finaltick参数也暗示了只有最后一次tick起实效。
看来tick完全不需要了?
猜测的一些tick存在的可能原因:
a 仅仅调试方便;
b 对于帧间隔时间太大的情况必须分小片处理
c 服务器与客户端同步上的考虑
3 其它帧操作
3.1 客户端的全局变量计算赋值
CClientState cl
CGlobalVarsBase g_ClientGlobalVariables
3.2 CL_RunPrediction()
客户端的预测与模拟,具体不详。
3.3 CL_ApplyAddAngle()
客户端的Angle,是什么?
3.4 CL_ExtraMouseUpdate( extramousetime )
// Make sure mouse is not lagged regardless
调用g_ClientDLL->ExtraMouseSample()
估计鼠标需要一些特别处理,具体不详。
3.5 _Host_RunFrame_Render(),
_Host_RunFrame_Sound()
轮到渲染系统与声音系统真正工作了,有志者就钻进去看吧。
3.6 ClientDLL_Update()
// Allow client .dll to think
是客户端的预测与模拟,到底做些什么,我很想看看。
3.7 结束前的杂事
Host_Speeds() 显示系统有多快
就是将时间概要信息输出,主要是调试用。
Host_UpdateMapList()
调用g_MapListMgr.Think()检查一遍地图列表。
(用Think()命名是不是过分拟人化了?)
还有内存检查等事,忽略之。
代码混乱,我的废话也多。
为了收起脂肪,把肌肉显出来,代码可作以下改进:
1 Profile,还有其它调试用的代码收敛一些,
如下面的堆检查代码可用一个HOST_CHECKHEAP宏定义代替。
if ( host_checkheap )
{
#ifdef _WIN32
if ( _heapchk() != _HEAPOK )
{
Sys_Error( "_Host_RunFrame (top): _heapchk() != _HEAPOK\n" );
}
#endif
}
2 有关time与tick的代码须整理并合入一个函数。
3 tick循环理应提出为一个函数。
4 CL_XXXX函数,还有Cbuf_Execute()这些函数调用可以插入到某个
_Host_RunFrame_XXX()函数中或新建一个_Host_RunFrame_XXX()函数。 |
|