游戏开发论坛

 找回密码
 立即注册
搜索
查看: 3053|回复: 0

_Host_RunFrame()

[复制链接]

12

主题

19

帖子

19

积分

新手上路

Rank: 1

积分
19
发表于 2003-10-30 16:19:00 | 显示全部楼层 |阅读模式

_Host_RunFrame()是一个两百多行的特别婆婆妈妈的函数。
感觉象是一大堆稀稀拉拉牵牵扯扯的脂肪下水中夹杂了几丝精肉。
这些精肉估计是这几个Host_RunFrame_XXX()
及Host_XXX()函数,还有Cbuf_Execute()。
其它的罗嗦代码是处理时间,有time 及tick好几个变量,
这些时间有什么意义尚需探查。

先把这堆下水扯成几块,一块一块地理比较好。

函数的下半部分是一个&quotrofile 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()函数。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

作品发布|文章投稿|广告合作|关于本站|游戏开发论坛 ( 闽ICP备17032699号-3 )

GMT+8, 2025-6-16 19:29

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表