版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、经过一段时间的设计与完善,我们游戏的AI服务器已经达到了基本的性能要求,目前单个AI进程可同时运行4000+个频繁的AI对象。在前面一篇博客中已经提到过,AI服务器的主逻辑循环是单线程的,这个线程上运行了数千个用户级线程,每个用户级线程运行一个AI对象。AI对象被激活之后就会运行一段lua脚本,以实现AI逻辑.之所以采用用户级线程(windows下是fiber,linux下使用ucontext)的方案,是因为AI的实现使用了大量的远程调用,如果使用同步调用势必导致主线程的阻塞,从而影响AI服务器的性能。采用异步调用又导致了逻辑的过分复杂。而用户级线程正好解决了这些问题,向上提供了一个同步调用的
2、接口,又不会导致主线程的阻塞(当一个用户级线程处于等待结果的状态下,调度器可以选择另一个用户级线程来运行)。AI服务器的主要构件是用户级线程调度器,和一个用户级线程池,服务器启动后会产生一组用户级线程序,并且在每个线程上创建一个lua虚拟机。基本的设计思路已经介绍完毕,下面介绍各个主要的组成部分:首先是主循环:view plain1. voidCAIApp:Process()2. 3. psarmorl_pa(*this);4. Scheduler:Init();5. while(!GetExitTaskFlag()&l_pa(psobj:realtime)6. 7. /如果到game的连接断
3、开,执行错误处理并尝试重连8. while(!m_flag2Game)9. 10. /连接断了,要清除所有已经绑定的Ai对象11. /g_AiObjMap为空的话不可能有任务在运行12. if(!g_AiObjMap.empty()13. 14. /连接已经断开,停掉所有运行的AI15. 16. std:mapuLong,rptr:iteratorit=g_AiObjMap.begin();17. std:mapuLong,rptr:iteratorend=g_AiObjMap.end();18. for(;it!=end;+it)19. it-second-StopAi();20. 21.
4、/清理active列表22. Scheduler:ClearActiveList();23. /清理timeout列表24. Scheduler:ClearTimeOut();25. 26. std:cout到gameserver的连接断开,清除所有绑定对象std:endl;27. std:mapuLong,rptr:iteratorit=g_AiObjMap.begin();28. std:mapuLong,rptr:iteratorend=g_AiObjMap.end();29. for(;it!=end;+it)30. it-second=0;31. g_AiObjMap.clear()
5、;32. 33. /清理aigroup34. 35. std:maplong,rptr:iteratorit=g_GroupMap.begin();36. std:maplong,rptr:iteratorend=g_GroupMap.end();37. for(;it!=end;+it)38. it-second=0;39. g_GroupMap.clear();40. 41. 42. m_pToGame=0;43. while(m_pToGame._nil()44. 45. rptrl_sock=g_aiapp-Connect(g_aiapp-m_config.m_gameip,g_aia
6、pp-m_config.m_gameport);46. if(l_sock._nil()47. 48. std:cout连接game失败!5秒后重试.GetWPacket();54. l_wpk.WriteCmd(CMD_AM_AILOGIN);55. l_wpk.WriteShort(g_aiapp-m_config.m_mapcount);56. for(inti=0;im_config.m_mapcount;+i)57. 58. l_wpk.WriteString(g_aiapp-m_config.m_namesi.c_str();59. 60. l_sock-SendData(l_wp
7、k);61. m_pToGame=l_sock;62. m_flag2Game=true;63. break;64. 65. Sleep(5000);66. 67. 68. Scheduler:Schedule();69. PeekPacket(50);70. 71. Scheduler:Destroy();72. 73. voidCAIApp:Process()74. 75. psarmorl_pa(*this);76. Scheduler:Init();77. while(!GetExitTaskFlag()&l_pa(psobj:realtime)78. 79. /如果到game的连接断
8、开,执行错误处理并尝试重连80. while(!m_flag2Game)81. 82. /连接断了,要清除所有已经绑定的Ai对象83. /g_AiObjMap为空的话不可能有任务在运行84. if(!g_AiObjMap.empty()85. 86. /连接已经断开,停掉所有运行的AI87. 88. std:mapuLong,rptr:iteratorit=g_AiObjMap.begin();89. std:mapuLong,rptr:iteratorend=g_AiObjMap.end();90. for(;it!=end;+it)91. it-second-StopAi();92. 93
9、. /清理active列表94. Scheduler:ClearActiveList();95. /清理timeout列表96. Scheduler:ClearTimeOut();97. 98. std:cout到gameserver的连接断开,清除所有绑定对象std:endl;99. std:mapuLong,rptr:iteratorit=g_AiObjMap.begin();100. std:mapuLong,rptr:iteratorend=g_AiObjMap.end();101. for(;it!=end;+it)102. it-second=0;103. g_AiObjMap.c
10、lear();104. 105. /清理aigroup106. 107. std:maplong,rptr:iteratorit=g_GroupMap.begin();108. std:maplong,rptr:iteratorend=g_GroupMap.end();109. for(;it!=end;+it)110. it-second=0;111. g_GroupMap.clear();112. 113. 114. m_pToGame=0;115. while(m_pToGame._nil()116. 117. rptrl_sock=g_aiapp-Connect(g_aiapp-m_c
11、onfig.m_gameip,g_aiapp-m_config.m_gameport);118. if(l_sock._nil()119. 120. std:cout连接game失败!5秒后重试.GetWPacket();126. l_wpk.WriteCmd(CMD_AM_AILOGIN);127. l_wpk.WriteShort(g_aiapp-m_config.m_mapcount);128. for(inti=0;im_config.m_mapcount;+i)129. 130. l_wpk.WriteString(g_aiapp-m_config.m_namesi.c_str();
12、131. 132. l_sock-SendData(l_wpk);133. m_pToGame=l_sock;134. m_flag2Game=true;135. break;136. 137. Sleep(5000);138. 139. 140. Scheduler:Schedule();141. PeekPacket(50);142. 143. Scheduler:Destroy();144. 上面代码的主要作用就是尝试连接gameserver,如果连接成功就在循环中调用调度器的调度函数以选择合适的用户级线程运行。PeekPacket(50);会从网络层提取网络包,如果没有网络包则会休眠最
13、多50毫秒.下面在来看看调度器:view plain1. voidScheduler:Schedule()2. 3. /将所有等待添加到m_activeList中的纤程都添加进去4. 5. for(unsignedinti=0;iSetNext(0);9. if(m_active_tail)10. 11. m_active_tail-SetNext(ut);12. m_active_tail=ut;13. 14. else15. 16. m_active_head=m_active_tail=ut;17. 18. 19. pending_index=0;20. 21. uthread*cur=
14、m_active_head;22. uthread*pre=NULL;23. while(cur)24. 25. g_aiapp-PeekPacket(0);26. m_curuid=cur-GetUid();27. SwitchToFiber(cur-GetUContext();28. m_curuid=-1;29. unsignedcharstatus=cur-GetStatus();30. /当纤程处于以下状态时需要从可运行队列中移除31. if(status=DEAD|status=SLEEP|status=WAIT4EVENT|status=UNACTIVED|status=YIEL
15、D)32. 33. /删除首元素34. if(cur=m_active_head)35. 36. /同时也是尾元素37. if(cur=m_active_tail)38. m_active_head=m_active_tail=NULL;39. else40. m_active_head=cur-Next();41. 42. elseif(cur=m_active_tail)43. 44. pre-SetNext(NULL);45. m_active_tail=pre;46. 47. else48. pre-SetNext(cur-Next();49. uthread*tmp=cur;50.
16、cur=cur-Next();51. tmp-SetNext(0);52. /如果仅仅是让出处理器,需要重新投入到可运行队列中53. if(status=YIELD)54. Add2Active(tmp);55. 56. 57. else58. 59. pre=cur;60. cur=cur-Next();61. 62. 63. /看看有没有timeout的纤程64. 65. uLongnow=dbc:GetTickCount();66. while(m_timeoutlist.Min()!=0&m_timeoutlist.Min()ut-GetStatus()=WAIT4EVENT|time
17、out-ut-GetStatus()=SLEEP)70. 71. timeout-ut-wakeuptick=timeout-_timeout;72. Add2Active(timeout-ut);73. 74. 75. 76. 77. voidScheduler:Schedule()78. 79. /将所有等待添加到m_activeList中的纤程都添加进去80. 81. for(unsignedinti=0;iSetNext(0);85. if(m_active_tail)86. 87. m_active_tail-SetNext(ut);88. m_active_tail=ut;89.
18、90. else91. 92. m_active_head=m_active_tail=ut;93. 94. 95. pending_index=0;96. 97. uthread*cur=m_active_head;98. uthread*pre=NULL;99. while(cur)100. 101. g_aiapp-PeekPacket(0);102. m_curuid=cur-GetUid();103. SwitchToFiber(cur-GetUContext();104. m_curuid=-1;105. unsignedcharstatus=cur-GetStatus();106
19、. /当纤程处于以下状态时需要从可运行队列中移除107. if(status=DEAD|status=SLEEP|status=WAIT4EVENT|status=UNACTIVED|status=YIELD)108. 109. /删除首元素110. if(cur=m_active_head)111. 112. /同时也是尾元素113. if(cur=m_active_tail)114. m_active_head=m_active_tail=NULL;115. else116. m_active_head=cur-Next();117. 118. elseif(cur=m_active_ta
20、il)119. 120. pre-SetNext(NULL);121. m_active_tail=pre;122. 123. else124. pre-SetNext(cur-Next();125. uthread*tmp=cur;126. cur=cur-Next();127. tmp-SetNext(0);128. /如果仅仅是让出处理器,需要重新投入到可运行队列中129. if(status=YIELD)130. Add2Active(tmp);131. 132. 133. else134. 135. pre=cur;136. cur=cur-Next();137. 138. 139.
21、 /看看有没有timeout的纤程140. 141. uLongnow=dbc:GetTickCount();142. while(m_timeoutlist.Min()!=0&m_timeoutlist.Min()ut-GetStatus()=WAIT4EVENT|timeout-ut-GetStatus()=SLEEP)146. 147. timeout-ut-wakeuptick=timeout-_timeout;148. Add2Active(timeout-ut);149. 150. 151. 152. 调度器首先将重新处于激活态的线程投入到运行队列中,然后遍历可运行队列,运行其中的
22、线程,调度器的最后将处理所有处于休眠状态的线程,如果线程的休眠时间到了,则将线程重新投入到可运行队列中。在这里使用了一个极小堆来处理超时。从上面的代码可以看出,当调度器挑选了一个线程运行之后,代码路径就跳转到线程中,当线程需要阻塞时,就会设置一个状态(YIELD, WAIT4EVENT或SLEEP)并将运行权又重新交回给调度器,当调度器重新获得运行权后,代码会从SwitchToFiber(cur-GetUContext();中返回,调度器需要根据上次运行的线程的状态,或者将线程投入休眠队列(SLEEP),或者重新将线程投入到队列的末尾(YIELD)或者从运行队列中删除(WAIT4EVENT).
23、下面再看看一个同步调用的例子:以移动为例,假设AI请求移动到某给位置,则需要向gameserver发送移动请求,直到到达目标点,或者发现移动失败才会从调用中返回:view plain1. intAiAvatar:Move(Point3D&pt,shortcntx,uLongms)2. 3. classPosBlock:publicBlockStruct4. 5. public:6. PosBlock(Point3D&pos,AiAvatar*ava)7. :m_ava(ava),m_targetpos(pos)8. 9. /返回true则纤程从阻塞中恢复10. boolWakeUp()11.
24、12. /到达了请求点,恢复13. if(m_targetpos.x=m_ava-GetPos().x&14. m_targetpos.y=m_ava-GetPos().y)15. 16. returntrue;17. 18. returnfalse;19. 20. private:21. Point3Dm_targetpos;22. AiAvatar*m_ava;23. ;24. /printf(开始移动/n);25. /向GameServer发送移动请求26. WPacketl_wpk=g_aiapp-GetWPacket();27. l_wpk.WriteCmd(CMD_AM_BEGMO
25、V);28. l_wpk.WriteLong(pt.x);29. l_wpk.WriteLong(pt.y);30. l_wpk.WriteLong(pt.z);31. l_wpk.WriteShort(cntx);32. Send2Game(this,l_wpk);33. /阻塞所在fiber直到pos到达要求的值/或者收到移动失败消息/或则AI被请求停止34. PosBlockpb(pt,this);35. Scheduler:Block(&pb,ms);36. /接到了停止AI的命令37. if(!isAiRunning()38. return-1;39. boolret=(pt.x=m
26、_pos.x&pt.y=m_pos.y);40. returnret?1:0;41. 42. intAiAvatar:Move(Point3D&pt,shortcntx,uLongms)43. 44. classPosBlock:publicBlockStruct45. 46. public:47. PosBlock(Point3D&pos,AiAvatar*ava)48. :m_ava(ava),m_targetpos(pos)49. 50. /返回true则纤程从阻塞中恢复51. boolWakeUp()52. 53. /到达了请求点,恢复54. if(m_targetpos.x=m_av
27、a-GetPos().x&55. m_targetpos.y=m_ava-GetPos().y)56. 57. returntrue;58. 59. returnfalse;60. 61. private:62. Point3Dm_targetpos;63. AiAvatar*m_ava;64. ;65. /printf(开始移动/n);66. /向GameServer发送移动请求67. WPacketl_wpk=g_aiapp-GetWPacket();68. l_wpk.WriteCmd(CMD_AM_BEGMOV);69. l_wpk.WriteLong(pt.x);70. l_wpk.
28、WriteLong(pt.y);71. l_wpk.WriteLong(pt.z);72. l_wpk.WriteShort(cntx);73. Send2Game(this,l_wpk);74. /阻塞所在fiber直到pos到达要求的值/或者收到移动失败消息/或则AI被请求停止75. PosBlockpb(pt,this);76. Scheduler:Block(&pb,ms);77. /接到了停止AI的命令78. if(!isAiRunning()79. return-1;80. boolret=(pt.x=m_pos.x&pt.y=m_pos.y);81. returnret?1:0;
29、82. 函数首先创建了一个阻塞条件的结构,然后阻塞在这个条件上,在这里是判断AI对象是否到达了目标点。然后将移动请求发送出去并阻塞在条件上。当gameserver把对象移动到正确的点之后,会把对象的坐标通过网络同步到AI服务器,处理网络包的时候发现那个对象对应的线程正被阻塞,就会调用阻塞条件的WakeUp函数尝试唤醒线程,此时如果条件满足,WakeUp就会返回true,线程被重新投入到可运行队列中,否则线程就会继续被阻塞。最用来看一段AI脚本,当一个AI对象被激活(进入玩家的视野),就会为这个对象分配一个线程,这个线程就会马上运行与这个对象相关的lua入口函数:view plain1. fun
30、ctionmonster_routine(this)2. -出生点3. localstart_pos=4. start_pos.x,start_pos.y,start_pos.z=getbegpos(this)5. 6. localc=17. 8. -巡逻点9. localpoints=10. x=start_pos.x+300,y=start_pos.y,z=start_pos.z,11. x=start_pos.x,y=start_pos.y,z=start_pos.z12. 13. 14. 15. -生成状态机16. stateMachine=AiStateMachine:new()17
31、. stateMachine.owner=this18. -初始化trace19. stateMachine.state_trace=trace:new():init(this,stateMachine,start_pos)20. -stateMachine.state_trace:init(this,stateMachine,start_pos)21. -初始化partol22. stateMachine.state_partol=partol:new():init(this,stateMachine,start_pos,points)23. -stateMachine.state_part
32、ol:init(this,stateMachine,start_pos,points)24. -初始化attack25. stateMachine.state_attack=attack:new():init(this,stateMachine)26. -stateMachine.state_attack:init(this,stateMachine)27. -初始化goback28. stateMachine.state_goback=goback:new():init(this,stateMachine,start_pos)29. -stateMachine.state_goback:in
33、it(this,stateMachine,start_pos)30. -初始化help31. stateMachine.state_help=help:new():init(this,stateMachine)32. -stateMachine.state_help:init(this,stateMachine)33. 34. stateMachine.cur_state=stateMachine.state_partol35. 36. whileisAiRunning(this)=truedo37. 38. ifisdead(this)=truethen39. sc_yield()40. e
34、lse41. stateMachine.cur_pos.x,stateMachine.cur_pos.y,stateMachine.cur_pos.z=getpos(this)42. stateMachine.target=get_target(this)43. ifstateMachine.target=nilthen44. stateMachine.target=select_target(this)45. end46. 47. 48. -查看是否有消息要处理49. localsender50. localrecver51. localmsg52. localsendtick53. sen
35、der,recver,msg,sendtick=PopMsg(this)54. ifsender=nilthen55. print(消息队列非空)56. ifmsg=helpthen57. -如果自己没有目标才处理帮助请求58. ifstateMachine.target=nilthen59. stateMachine.target=sender60. stateMachine.cur_state=stateMachine.state_help61. end62. end63. end64. 65. localret=066. ret,stateMachine.cur_state=stateM
36、achine.cur_state:execute()67. ifret=-1then68. return69. end70. sc_yield()71. end72. 73. end74. end75. functionmonster_routine(this)76. -出生点77. localstart_pos=78. start_pos.x,start_pos.y,start_pos.z=getbegpos(this)79. 80. localc=181. 82. -巡逻点83. localpoints=84. x=start_pos.x+300,y=start_pos.y,z=start_pos.z,85. x=start_pos.x,y=start_pos.y,z=start_pos.z86. 87. 88. 89. -生成状态机90. stateMachine=A
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2026年朔州陶瓷职业技术学院单招综合素质考试题库及答案详细解析
- 2026年成都航空职业技术学院单招职业技能考试题库附答案详细解析
- 2026年四川建筑职业技术学院单招综合素质考试题库含答案详细解析
- 2026上半年四川事业单位统考乐山市考试招聘636人笔试模拟试题及答案解析
- 2026年内蒙古自治区包头市高职单招职业技能考试题库附答案详细解析
- 2026年重庆文化艺术职业学院单招职业适应性测试题库含答案详细解析
- 2026年云南省昆明市高职单招职业技能考试题库附答案详细解析
- 2026中国东方演艺集团有限公司子公司东方歌舞团有限公司总经理、副总经理岗位招聘3人备考题库及参考答案详解(培优b卷)
- 2026年苏州工业职业技术学院单招职业技能考试题库附答案详细解析
- 桥梁钢结构安装技术方案
- 酒店咨询服务方案模板
- DB14-T 2779-2023营造林工程监理规范
- 9.2.1 用坐标表示地理位置 说课稿 2024-2025学年人教版数学七年级下册
- 加油站片区经理能力提升培训
- 老旧小区改造的国内外现状与发展趋势
- 口腔冠髓切断术
- 首件确认管理办法
- Q-JJJ 9002-2025 铁路建设项目安全穿透式管理实施指南
- 公共区域活动管理办法
- 高三二轮复习生物种群群落生态系统微专题课件
- 2025年中考数学压轴专题汇编(江苏专用)压轴专题09定角定高模型(原卷版+解析)
评论
0/150
提交评论