经过一段时间的设计与完善,我们游戏的AI服务器已经达到了基本的性能要求,目前单个AI进程可同时运行4000+个频繁的AI对象。在前面一篇博客中已经提到过,AI服务器的主逻辑循环是单线程的,这个线程上运行了数千个用户级线程,每个用户级线程运行一个AI对象。AI对象被激活之后就会运行一段lua脚本,以实现AI逻辑.之所以采用用户级线程(windows下是fiber,linux下使用ucontext)的方案,是因为AI的实现使用了大量的远程调用,如果使用同步调用势必导致主线程的阻塞,从而影响AI服务器的性能。采用异步调用又导致了逻辑的过分复杂。而用户级线程正好解决了这些问题,向上提供了一个同步调用的接口,又不会导致主线程的阻塞(当一个用户级线程处于等待结果的状态下,调度器可以选择另一个用户级线程来运行)。AI服务器的主要构件是用户级线程调度器,和一个用户级线程池,服务器启动后会产生一组用户级线程序,并且在每个线程上创建一个lua虚拟机。基本的设计思路已经介绍完毕,下面介绍各个主要的组成部分:首先是主循环:viewplain1.voidCAIApp::Process()2.{3.psarmorl_pa(*this);4.Scheduler::Init();5.while(!GetExitTaskFlag()&&l_pa(psobj::realtime))6.{7.//如果到game的连接断开,执行错误处理并尝试重连8.while(!m_flag2Game)9.{10.//连接断了,要清除所有已经绑定的Ai对象11.//g_AiObjMap为空的话不可能有任务在运行12.if(!g_AiObjMap.empty())13.{14.//连接已经断开,停掉所有运行的AI15.{16.std::mapuLong,rptrAiAvatar::iteratorit=g_AiObjMap.begin();17.std::mapuLong,rptrAiAvatar::iteratorend=g_AiObjMap.end();18.for(;it!=end;++it)19.it-second-StopAi();20.}21.//清理active列表22.Scheduler::ClearActiveList();23.//清理timeout列表24.Scheduler::ClearTimeOut();25.{26.std::cout到gameserver的连接断开,清除所有绑定对象std::endl;27.std::mapuLong,rptrAiAvatar::iteratorit=g_AiObjMap.begin();28.std::mapuLong,rptrAiAvatar::iteratorend=g_AiObjMap.end();29.for(;it!=end;++it)30.it-second=0;31.g_AiObjMap.clear();32.}33.//清理aigroup34.{35.std::maplong,rptrAiGroup::iteratorit=g_GroupMap.begin();36.std::maplong,rptrAiGroup::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.rptrDataSocketl_sock=g_aiapp-Connect(g_aiapp-m_config.m_gameip,g_aiapp-m_config.m_gameport);46.if(l_sock._nil())47.{48.std::cout连接game失败!5秒后重试...std::endl;49.}50.else51.{52.printf(连接game成功...);53.WPacketl_wpk=g_aiapp-GetWPacket();54.l_wpk.WriteCmd(CMD_AM_AILOGIN);55.l_wpk.WriteShort(g_aiapp-m_config.m_mapcount);56.for(inti=0;ig_aiapp-m_config.m_mapcount;++i)57.{58.l_wpk.WriteString(g_aiapp-m_config.m_names[i].c_str());59.}60.l_sock-SendData(l_wpk);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的连接断开,执行错误处理并尝试重连80.while(!m_flag2Game)81.{82.//连接断了,要清除所有已经绑定的Ai对象83.//g_AiObjMap为空的话不可能有任务在运行84.if(!g_AiObjMap.empty())85.{86.//连接已经断开,停掉所有运行的AI87.{88.std::mapuLong,rptrAiAvatar::iteratorit=g_AiObjMap.begin();89.std::mapuLong,rptrAiAvatar::iteratorend=g_AiObjMap.end();90.for(;it!=end;++it)91.it-second-StopAi();92.}93.//清理active列表94.Scheduler::ClearActiveList();95.//清理timeout列表96.Scheduler::ClearTimeOut();97.{98.std::cout到gameserver的连接断开,清除所有绑定对象std::endl;99.std::mapuLong,rptrAiAvatar::iteratorit=g_AiObjMap.begin();100.std::mapuLong,rptrAiAvatar::iteratorend=g_AiObjMap.end();101.for(;it!=end;++it)102.it-second=0;103.g_AiObjMap.clear();104.}105.//清理aigroup106.{107.std::maplong,rptrAiGroup::iteratorit=g_GroupMap.begin();108.std::maplong,rptrAiGroup::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.rptrDataSocketl_sock=g_aiapp-Connect(g_aiapp-m_config.m_gameip,g_aiapp-m_config.m_gameport);118.if(l_sock._nil())119.{120.std::cout连接game失败!5秒后重试...std::endl;121.}122.else123.{124.printf(连接game成功...);125.WPacketl_wpk=g_aiapp-GetWPacket();126.l_wpk.WriteCmd(CMD_AM_AILOGIN);127.l_wpk.WriteShort(g_aiapp-m_config.m_mapcount);128.for(inti=0;ig_aiapp-m_config.m_mapcount;++i)129.{130.l_wpk.WriteString(g_aiapp-m_config.m_names[i].c_str());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);会从网络层提取网络包,如果没有网络包则会休眠最多50毫秒.下面在来看看调度器:viewplain1.voidScheduler::Schedule()2.{3.//将所有等待添加到m_activeList中的纤程都添加进去4.{5.for(unsignedinti=0;ipending_index;++i)6.{7.uthread*ut=m_uthreads[m_pendingAdd[i]];8.ut-SetNext(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=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==YIELD)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.cur=cur-Next();51.tmp-SetNext(0);52.//如果仅仅是让出处理器,需要重新投入到可运行队列中53.if(statu