你说的,玩家发来攻击、聊天的数据后,也提交一个事件到队列。
我觉得这两个东西首先不应该放到一个队列,其次在处理的时候不应该遍历队列。
关于第一点:
攻击和聊天本来就应该是两个不同模块的职责。而且优先级也不一样。
在MMORPG中,攻击是一个实时的操作,你必须以最快的速度发送给客户端,
一般来说,在MMORPG中,客户端在接受到可以攻击的发包后,才会做出攻击反应。当然也有客户端预测的,不过前者毕竟是主流而且严谨。
聊天是一个次要的操作,譬如从发起者到接受者之间,有个2秒的时间差,都是可以接受的。
关于第二点:
我不太明白你为什么需要遍历,你说有大量的锁定遍历操作更是让我云中雾里。
我觉得一个好友都会以decorate模式或者类似模式存在于角色数据结构中,这个数据结构应该包括客户端和服务器的。在MMORPG中,瘦客户端已经越来越不流行了。所以客户端也会有这个角色数据结构。
而通讯的时候仅需要使用这个结构列队中的索引即可。
譬如
aaa对bbb说你好。
那么这个包的形式可能是:
发言类型
发言人ID
发言人名称
接受者ID
接受者名称
发言内容
当然,客户端的玩家数据结构并意味一次发送过来的,这样没有必要并且没有效率。使用的缓式加载即可。
同样,问题里面的玩家攻击也是需要如此编号的:)
个人看法:
我觉得你的做法有问题的
你说的,玩家发来攻击、聊天的数据后,也提交一个事件到队列。
我觉得这两个东西首先不应该放到一个队列,其次在处理的时候不应该遍历队列。
关于第一点:
攻击和聊天本来就应该是两个不同模块的职责。而且优先级也不一样。
在MMORPG中,攻击是一个实时的操作,你必须以最快的速度发送给客户端,
一般来说,在MMORPG中,客户端在接受到可以攻击的发包后,才会做出攻击反应。当然也有客户端预测的,不过前者毕竟是主流而且严谨。
聊天是一个次要的操作,譬如从发起者到接受者之间,有个2秒的时间差,都是可以接受的。
关于第二点:
我不太明白你为什么需要遍历,你说有大量的锁定遍历操作更是让我云中雾里。
我觉得一个好友都会以decorate模式或者类似模式存在于角色数据结构中,这个数据结构应该包括客户端和服务器的。在MMORPG中,瘦客户端已经越来越不流行了。所以客户端也会有这个角色数据结构。
而通讯的时候仅需要使用这个结构列队中的索引即可。
譬如
aaa对bbb说你好。
那么这个包的形式可能是:
发言类型
发言人ID
发言人名称
接受者ID
接受者名称
发言内容 ghost...@gmail.com
Web : http://www.GhostSoft.net
遍历在服务器是一个坚决避免的操作阿。
在我的印象里,只有发全区通告之类的必要操作才会进行遍历。
我觉得你应该更改你的线性遍历,至少把那个链表改成std::map,二叉树的查询方式也是可以的。
至于你的聊天和攻击,我的建议是不要放到一个列队中,至少以我的看法,他们都不可能同时出现,
因为在两个不同的模块,不同的线程中嘛。:)
而且你所谓的避免多线程之间的锁定,我真的不太明白,因为我想不到为什么需要使用锁定。
提到锁定,我可以告诉你,虽然我没有测试过锁定操作(指关键区)的时间,但是我曾经做过服务器内存池系统,
我发现,当分配内存时,使用锁定比没有使用锁定的整体效率(注意是整体效率)差不多慢了10~20倍。
当然,除非你的客户端具有攻击预测或者服务器不需要支持太多的人数,这些也是可以考虑的:)
啊?
难道你的角色没有包含session?或者能得到session并且算法复杂度为O(1)的操作?
遍历在服务器是一个坚决避免的操作阿。
在我的印象里,只有发全区通告之类的必要操作才会进行遍历。
我觉得你应该更改你的线性遍历,至少把那个链表改成std::map,二叉树的查询方式也是可以的。
至于你的聊天和攻击,我的建议是不要放到一个列队中,至少以我的看法,他们都不可能同时出现,
因为在两个不同的模块,不同的线程中嘛。:)
而且你所谓的避免多线程之间的锁定,我真的不太明白,因为我想不到为什么需要使用锁定。
提到锁定,我可以告诉你,虽然我没有测试过锁定操作(指关键区)的时间,但是我曾经做过服务器内存池系统,
呵呵~然后再说重要问题吧
因为我觉得在你的描述中总是出现链表,那么这个大家都应该清楚了:)
std::vector是O( 1 )
std::map是O( log2N )
std::list则是O( l )
所以你说的,
当一个玩家连接进来后,总需要给他分配一个session的结构吧?这个结构中包含玩家信息、socket、以及其他需要的数据。
然后总要把这个session记录下来吧,所以必须插进一个链表。
这里的lock我是同意的,(曾经想过取代的方法,但是感觉都不是很保险:))
但把session插入一个链表我却认为可以把session插入一个列队:)
这个列队是线性增长的,而这个列队中的索引,就是这个session的编号,就作为角色的动态ID。
而所有客户端和游戏服务器通讯时候使用的,就是这个动态ID。
那么无论你怎么样,都是O(1)操作了:)
对于你说的,心跳线程循环检测超时没有数据的socket,就需要遍历链表。
以前的项目中我们的确有类似的操作,
不过现在的项目去掉了,似乎意义不大,直接使用的是socket产生的事件,呵呵:)
链表的查询复杂度是o(n)
to all:
如果不能用数组存储session信息的话,
我是说用socket当下标,那就改用hash
map吧,性能紧次于数组,比map快。
>>链表的查询复杂度是o(n)
我觉得你单独有一个聊天服务器的话也可以,哪怕是线性搜索,也就无所谓了:)
STL的Map已经很快了。
他就使用红黑树实现的阿
首先根本不用数组的实现为10000000,没有任何意义。
你把他定义到8192都已经很不错了:)
说一个很实际的问题,我以前的一个公司在2003年的一个成功运营的MMORPG项目中,这个值是800。。。。
其次你说的
这3个数据结构,只有list是插入、删除速度最快的,符合服务器需要,即使使用tree结构,可以优化查找,但是插入的速度却慢下来了。
我不这么认为,我认为,vector的插入,删除速度是最快的。
至少他会比链表少几个赋值操作:)
回复 | 答复作者 | 转发 | 打印 | 显示个别帖子 |
显示原始邮件 | 报告滥用行为 | 查找此作者的帖子
我的项目有单独的聊天服务器,目的是把场景相关(如战斗)和场景无关(如聊天)的功能隔离。
从玩家ID找到session恐怕只能完全搜索了,除非你的ID就是socket,也就是玩家上线后自动给他分配一个本次会话才有效的ID。可惜我的项目没法这么作,我的ID是unsigned
__int64的。
------------------------
对于你的这段话我有一点不明白,动态的分配一个会话ID,当然是可以的。
和具体项目是什么应该没有关系吧。
To Ghost Cheng -
首先根本不用数组的实现为10000000,没有任何意义。
你把他定义到8192都已经很不错了:)
说一个很实际的问题,我以前的一个公司在2003年的一个成功运营的MMORPG项目中,这个值是800。。。。
关于聊天设置的问题
"收到玩家A的聊天数据,要转发给ID为999的玩家B"那么潜在的含义是聊天是一对一聊。
但是在MMORPG中组队聊天是很正常的一个功能。换句话说。现有的设计不支持组队聊天。
Hi yimlin:接受建议,呵呵其实这个问题,我一直在考虑,因为如果单独使用聊天服务器的话,那么玩家直接公开的聊天内容就会存在一个范围的问题.因为玩家的公开聊天内容,只有周围的玩家才能看见,所以这中方式的聊天,需要判断周围玩家的位置.如果使用聊天服务器,那么.要么把这个功能,脱离聊天服务器,仍然靠游戏服务器转发,要么就必须让游戏服务器实时通知聊天服务器,每个玩家的坐标这个问题,我正在考虑中.
On 3/7/06, yimlin <yim...@gmail.com> wrote:
关于聊天设置的问题
"收到玩家A的聊天数据,要转发给ID为999的玩家B"那么潜在的含义是聊天是一对一聊。
但是在MMORPG中组队聊天是很正常的一个功能。换句话说。现有的设计不支持组队聊天。
--
Sincerely,
Ghost Cheng
Email : ghost...@gmail.com
Web : http://www.GhostSoft.net
我不明白为什么提到删除,你的意思就是把他从列队中去掉?
对于服务器来说,很多时候都是要自定义一些数据结构的。
举个简单的例子
int nCurIndex = 0;
char szBuffer[1000] = { 0 };
bool AddChar( char c )
{
int nIndex = GetAvailIndex( );
if ( nIndex == -1 )
{
return false;
}
szBuffer[nIndex] = c;
return true;
}
void del( int nIndex )
{
szBuffer[nIndex] = 0;
}
int GetAvailIndex( )
{
while( nCurIndex ++ )
{
if ( nCurIndex > 1000 )
{
nCurIndex = -1;
break;
}
if ( !szBuffer[nCurIndex] )
return nCurIndex;
}
return -1;
}
当然这仅仅是一个随手写的大概思想,真正的要比这个复杂一些:(
第二个,这就要看你怎么管理你的动态ID了,也就是服务器程序员的工作了,否则服务器程序员岂不是很轻松:)因为在我的感觉下,服务器程序员远远比客户端程序员轻松,也深有体会:)当然服务器逻辑程序员事情比较多一些:)
最后一个就是,我觉得没有必要在乎那几个字节,据我以前的项目观察,流量好像都不是什么问题,你需要减少的是通讯的次数,因为socket是I/O操作,一般来说,效率绝对不是在你多了那么几个字节上:)
当然,因为服务器设计比较复杂,很可能我的观点也是错误的。
即供参考:)
Web : http://www.GhostSoft.net
呵呵,你说的没错,聊天服务器应该改成"场景无关聊天服务器"或者"玩家关系服务器"更合适,它的作用就是处理那些和场景无关的功能,普通聊天(就是你说的只有周围人才能看到的),我们是放在场景服务器处理的。
放在"场景无关聊天服务器"的功能还有私聊、组队聊天、公会聊天、自建聊天室聊天等等,这些都是和场景无关的,但是信息量却非常大,如果放到场景服务器的话,玩家较多的时候,会严重影响高优先级的消息发送。
其实放在一个服务器也可以,那你的发送队列就要支持优先级,但是我不建议使用
stl 的
priority_queue,那样你在发送完高优先级的包之前就不能发送聊天消息了。比较理想的方式是按一定的比例发送,这个可能需要自己写一个了。
good luck.