AI服务器的设计与实现.docx
《AI服务器的设计与实现.docx》由会员分享,可在线阅读,更多相关《AI服务器的设计与实现.docx(25页珍藏版)》请在冰豆网上搜索。
AI服务器的设计与实现
经过一段时间的设计与完善,我们游戏的AI服务器已经达到了基本的性能要求,目前单个AI进程可同时运行4000+个频繁的AI对象。
在前面一篇博客中已经提到过,AI服务器的主逻辑循环是单线程的,这个线程上运行了数千个用户级线程,每个用户级线程运行一个AI对象。
AI对象被激活之后就会运行一段lua脚本,以实现AI逻辑.
之所以采用用户级线程(windows下是fiber,linux下使用ucontext)的方案,是因为AI的实现使用了大量的远程调用,如果使用同步调用势必导致主线程的阻塞,从而影响AI服务器的性能。
采用异步调用又导致了逻辑的过分复杂。
而用户级线程正好解决了这些问题,向上提供了一个同步调用的接口,又不会导致主线程的阻塞(当一个用户级线程处于等待结果的状态下,调度器可以选择另一个用户级线程来运行)。
AI服务器的主要构件是用户级线程调度器,和一个用户级线程池,服务器启动后会产生一组用户级线程序,并且在每个线程上创建一个lua虚拟机。
基本的设计思路已经介绍完毕,下面介绍各个主要的组成部分:
首先是主循环:
viewplain
1.void CAIApp:
:
Process()
2.{
3. psarmor l_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. //连接已经断开,停掉所有运行的AI
15. {
16. std:
:
map >:
:
iterator it = g_AiObjMap.begin();
17. std:
:
map >:
:
iterator end = 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:
:
map >:
:
iterator it = g_AiObjMap.begin();
28. std:
:
map >:
:
iterator end = g_AiObjMap.end();
29. for( ; it !
= end; ++it)
30. it->second = 0;
31. g_AiObjMap.clear();
32. }
33. //清理aigroup
34. {
35. std:
:
map >:
:
iterator it = g_GroupMap.begin();
36. std:
:
map >:
:
iterator end = 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. rptr l_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. else
51. {
52. printf("连接game成功...");
53. WPacket l_wpk =g_aiapp->GetWPacket();
54. l_wpk.WriteCmd(CMD_AM_AILOGIN);
55. l_wpk.WriteShort(g_aiapp->m_config.m_mapcount);
56. for( int i = 0;i < g_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.void CAIApp:
:
Process()
74.{
75. psarmor l_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. //连接已经断开,停掉所有运行的AI
87. {
88. std:
:
map >:
:
iterator it = g_AiObjMap.begin();
89. std:
:
map >:
:
iterator end = 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:
:
map >:
:
iterator it = g_AiObjMap.begin();
100. std:
:
map >:
:
iterator end = g_AiObjMap.end();
101. for( ; it !
= end; ++it)
102. it->second = 0;
103. g_AiObjMap.clear();
104. }
105. //清理aigroup
106. {
107. std:
:
map >:
:
iterator it = g_GroupMap.begin();
108. std:
:
map >:
:
iterator end = 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. rptr l_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. else
123. {
124. printf("连接game成功...");
125. WPacket l_wpk =g_aiapp->GetWPacket();
126. l_wpk.WriteCmd(CMD_AM_AILOGIN);
127. l_wpk.WriteShort(g_aiapp->m_config.m_mapcount);
128. for( int i = 0;i < g_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毫秒.
下面在来看看调度器:
viewplain
1.void Scheduler:
:
Schedule()
2.{
3. //将所有等待添加到m_activeList中的纤程都添加进去
4. {
5. for(unsigned int i = 0; i < pending_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. else
15. {
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. unsigned char status = 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. else
40. m_active_head = cur->Next();
41. }
42. else if(cur == m_active_tail)
43. {
44. pre->SetNext(NULL);
45. m_active_tail = pre;
46. }
47. else
48. pre->SetNext(cur->Next());
49. uthread *tmp = cur;
50. cur = cur->Next();
51. tmp->SetNext(0);
52. //如果仅仅是让出处理器,需要重新投入到可运行队列中
53. if(status == YIELD)
54. Add2Active(tmp);
55.
56. }
57. else
58. {
59. pre = cur;
60. cur = cur->Next();
61. }
62. }
63. //看看有没有timeout的纤程
64. {
65. uLong now = dbc:
:
GetTickCount();
66. while(m_timeoutlist.Min() !
=0 && m_timeoutlist.Min() <= now)
67. {
68. st_timeout *timeout = m_timeoutlist.PopMin();
69. if(timeout->ut->GetStatus() == WAIT4EVENT || timeout->ut->GetStatus() == SLEEP)
70. {
71. timeout->ut->wakeuptick = timeout->_timeout;
72. Add2Active(timeout->ut);
73. }
74. }
75. }
76.}
77.void Scheduler:
:
Schedule()
78.{
79. //将所有等待添加到m_activeList中的纤程都添加进去
80. {
81. for(unsigned int i = 0; i < pending_index; ++i)
82. {
83. uthread *ut = m_uthreads[m_pendingAdd[i]];
84. ut->SetNext(0);
85. if(m_active_tail)
86. {
87. m_active_tail->SetNext(ut);
88. m_active_tail = ut;
89. }
90. else
91. {
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_