About MMORPG的逻辑层构架

50 views
Skip to first unread message

Ghost Cheng

unread,
Mar 5, 2006, 2:15:20 PM3/5/06
to dev4server
Hi all:
 
这两天maillist好像有点冷清了,我来立个靶子,大家讨论一下MMORPG的逻辑层构架。
 
所谓逻辑层构架,就是指MMORPG的跑地图、聊天转发、好友上线通知、交易事件等,
比如玩家或NPC跑地图的时候,以什么样的方式通知场景周围的玩家、转发聊天对话与好友上线通知的时候,如何才能尽量不去遍历玩家链表。
 
先说说我的想法,我处理的方式是基于EventEngine的,所谓EventEngine其实就是一个独立的线程,维护一个Event队列,
当对列中有事件的时候就处理。这里的事件包括:玩家动作(移动、攻击)、NPC动作(移动、攻击)、聊天、上线、下线等。
 
当数据包处理线程,收到玩家上线的数据包,就提交一个事件到队列,
同样,玩家发来攻击、聊天的数据后,也提交一个事件到队列。
NPC的事件触发时间,由另一个线程计算,一旦这个NPC到了需要移动或攻击的时候,就提交一个事件到队列。
 
这样确保所有的资源,都只有EventEngine一个线程访问,比如地图上的玩家链表等。
 
我遇到的问题:目前主要是聊天、或好友上线,这些事件处理的时候,需要遍历整个玩家链表,
这个链表就是网络层的session list,访问的时候需要锁定,如果有大量锁定遍历的操作,性能感觉会比较底,
不知道大家有什么好的方案?
 
希望大家踊跃发言哦!
 


--

Sincerely,
Ghost Cheng
Email : ghost...@gmail.com
Web : http://www.GhostSoft.net

疯子阿虹

unread,
Mar 5, 2006, 9:58:09 PM3/5/06
to 高性能网络编程邮件列表
个人看法:
我觉得你的做法有问题的

你说的,玩家发来攻击、聊天的数据后,也提交一个事件到队列。

我觉得这两个东西首先不应该放到一个队列,其次在处理的时候不应该遍历队列。

关于第一点:
攻击和聊天本来就应该是两个不同模块的职责。而且优先级也不一样。

在MMORPG中,攻击是一个实时的操作,你必须以最快的速度发送给客户端,
一般来说,在MMORPG中,客户端在接受到可以攻击的发包后,才会做出攻击反应。当然也有客户端预测的,不过前者毕竟是主流而且严谨。
聊天是一个次要的操作,譬如从发起者到接受者之间,有个2秒的时间差,都是可以接受的。

关于第二点:
我不太明白你为什么需要遍历,你说有大量的锁定遍历操作更是让我云中雾里。
我觉得一个好友都会以decorate模式或者类似模式存在于角色数据结构中,这个数据结构应该包括客户端和服务器的。在MMORPG中,瘦客户端已经越来越不流行了。所以客户端也会有这个角色数据结构。
而通讯的时候仅需要使用这个结构列队中的索引即可。
譬如
aaa对bbb说你好。
那么这个包的形式可能是:

发言类型
发言人ID
发言人名称
接受者ID
接受者名称
发言内容

当然,客户端的玩家数据结构并意味一次发送过来的,这样没有必要并且没有效率。使用的缓式加载即可。


同样,问题里面的玩家攻击也是需要如此编号的:)

Ghost Cheng

unread,
Mar 5, 2006, 10:10:20 PM3/5/06
to dev4s...@googlegroups.com
Hi 疯子阿虹:
 
我没有看懂你指的"好友都会以decorate模式或者类似模式存在于角色数据结构中"是什么意思。
我说的遍历链表,是指当A玩家发来一个聊天包(包含B玩家的ID,和聊天内容),告诉服务器,把这条聊天信息转发给B玩家,
这个时候,服务器需要在遍历玩家链表才能找到这个ID所对应的session对象,这时才能得到这个session的socket,才能进行转发。
 
好友上线的通知,也同样是这个问题,有N个玩家的好友列表里都有A玩家,当A玩家上线的时候,服务器同样也必须遍历链表,
找出,有哪些玩家的好友列表中有A玩家,这样才能发数据包通知他们,A玩家上线了。
 
我不知道你指的"好友都会以decorate模式或者类似模式存在于角色数据结构中"是如何避免遍历链表而直接得到指定玩家的socket的。
所有的查找关键字段,都仅仅是一个玩家ID而已,根据这个ID,如果不进行链表的遍历,我不知道还有什么办法,照到session对象的socket。
 
聊天和攻击,我放在一个队列里处理,是因为他们都需要遍历同一个链表,为了避免多线程,之间的锁定,所以放在一个队列里处理。
 
 


 
On 3/6/06, 疯子阿虹 <madma...@163.com> wrote:
个人看法:
我觉得你的做法有问题的

你说的,玩家发来攻击、聊天的数据后,也提交一个事件到队列。

我觉得这两个东西首先不应该放到一个队列,其次在处理的时候不应该遍历队列。

关于第一点:
攻击和聊天本来就应该是两个不同模块的职责。而且优先级也不一样。

在MMORPG中,攻击是一个实时的操作,你必须以最快的速度发送给客户端,
一般来说,在MMORPG中,客户端在接受到可以攻击的发包后,才会做出攻击反应。当然也有客户端预测的,不过前者毕竟是主流而且严谨。
聊天是一个次要的操作,譬如从发起者到接受者之间,有个2秒的时间差,都是可以接受的。

关于第二点:
我不太明白你为什么需要遍历,你说有大量的锁定遍历操作更是让我云中雾里。
我觉得一个好友都会以decorate模式或者类似模式存在于角色数据结构中,这个数据结构应该包括客户端和服务器的。在MMORPG中,瘦客户端已经越来越不流行了。所以客户端也会有这个角色数据结构。
而通讯的时候仅需要使用这个结构列队中的索引即可。
譬如
aaa对bbb说你好。
那么这个包的形式可能是:

发言类型
发言人ID
发言人名称
接受者ID
接受者名称

疯子阿虹

unread,
Mar 5, 2006, 10:28:43 PM3/5/06
to 高性能网络编程邮件列表

啊?
难道你的角色没有包含session?或者能得到session并且算法复杂度为O(1)的操作?

遍历在服务器是一个坚决避免的操作阿。
在我的印象里,只有发全区通告之类的必要操作才会进行遍历。

我觉得你应该更改你的线性遍历,至少把那个链表改成std::map,二叉树的查询方式也是可以的。

至于你的聊天和攻击,我的建议是不要放到一个列队中,至少以我的看法,他们都不可能同时出现,
因为在两个不同的模块,不同的线程中嘛。:)
而且你所谓的避免多线程之间的锁定,我真的不太明白,因为我想不到为什么需要使用锁定。
提到锁定,我可以告诉你,虽然我没有测试过锁定操作(指关键区)的时间,但是我曾经做过服务器内存池系统,
我发现,当分配内存时,使用锁定比没有使用锁定的整体效率(注意是整体效率)差不多慢了10~20倍。

当然,除非你的客户端具有攻击预测或者服务器不需要支持太多的人数,这些也是可以考虑的:)

疯子阿虹

unread,
Mar 5, 2006, 10:33:32 PM3/5/06
to 高性能网络编程邮件列表
还有就是你所谓的 A玩家上线 这种操作,
我对很疑惑不支持此功能的一个测试说了一句话:我们的目的不是QQ。
呵呵~不过后来我记得(有点忘记了)好像还是做了:)

Ghost Cheng

unread,
Mar 5, 2006, 10:41:41 PM3/5/06
to dev4s...@googlegroups.com
Hi 疯子阿虹:
 
我所指的"避免多线程之间的锁定"是这样的:
 
当一个玩家连接进来后,总需要给他分配一个session的结构吧?这个结构中包含玩家信息、socket、以及其他需要的数据。
然后总要把这个session记录下来吧,所以必须插进一个链表,因为网络层有多个线程,所以,插入链表的时候,肯定需要锁定。
好,现在这个链表里面有当前所有在线的玩家了,那么逻辑处理的线程,在这个链表查找玩家的时候,肯定也需要锁定它,
否则网络层在链表上做增加、删除操作,岂不是要崩溃?
 
另外,你说"难道你的角色没有包含session?"
不是这样的,实际上,我的角色对象就是session,角色对象是从session类继承下来的。
每个数据包到达的时候,我当然可以直接知道对应的角色,问题是如何使用一个包含在未知session中的ID,来找到这个未知的session。
我不知道有什么"能得到session并且算法复杂度为O(1)的操作",即便用std::map,也不是0(1)的速度阿,
愿听你详细指教一下。
 
另外,你说"遍历在服务器是一个坚决避免的操作阿。"
这句话我赞同,但是真正来说,未必能做的到,比如心跳线程循环检测超时没有数据的socket,就需要遍历链表。
 
On 3/6/06, 疯子阿虹 <madma...@163.com> wrote:

啊?
难道你的角色没有包含session?或者能得到session并且算法复杂度为O(1)的操作?

遍历在服务器是一个坚决避免的操作阿。
在我的印象里,只有发全区通告之类的必要操作才会进行遍历。

我觉得你应该更改你的线性遍历,至少把那个链表改成std::map,二叉树的查询方式也是可以的。

至于你的聊天和攻击,我的建议是不要放到一个列队中,至少以我的看法,他们都不可能同时出现,
因为在两个不同的模块,不同的线程中嘛。:)
而且你所谓的避免多线程之间的锁定,我真的不太明白,因为我想不到为什么需要使用锁定。
提到锁定,我可以告诉你,虽然我没有测试过锁定操作(指关键区)的时间,但是我曾经做过服务器内存池系统,

yimlin

unread,
Mar 6, 2006, 4:27:28 AM3/6/06
to 高性能网络编程邮件列表
我觉的你这个设计有问题。
聊天和动作不是一个队列,至少是两个队列。
好友上线可以利用数据库解决问题。
聊天也不需要遍历整个玩家列表。完全可以利用聊天室房间的概念来控制。

sunway

unread,
Mar 6, 2006, 4:40:58 AM3/6/06
to 高性能网络编程邮件列表
HI, Ghost Cheng:
说个我的看法,如果你这么设计下去,
一个MMORPG没办法写下去的。

closeall

unread,
Mar 6, 2006, 4:55:36 AM3/6/06
to dev4s...@googlegroups.com
to Ghost Cheng :
  
  我对这个队列锁定也很感兴趣,应该尽量少的在服务器端进行锁定。我今天下午偶然间在vckbase.net这个网站上看到了sodme 在2005年6月份对一份完成端口代码的评价。评价如下:
 
 sodme:还有个问题,EnterCriticalSection(&cInfoSection),你这样的互斥量使用是不是太频繁了?在我的方案中,由于采用了特殊的方法,根本没象你这样频繁地使用互斥量的。
(发表于2005-6-6 17:54:00)
 
不知道大宝是如何做的,呵呵.....

 
--
Closeall
MSN           : clos...@hotmail.com
Google Talk : closea...@gmail.com

疯子阿虹

unread,
Mar 6, 2006, 5:13:52 AM3/6/06
to 高性能网络编程邮件列表
首先先说一个无关紧要的问题吧,
你说的角色对象是从session类继承下来,
我不同意这么设计,继承是is-a,而session和角色之间的关系是has-a。
所以他们之间更应该使用类似decorate(装饰)这样的对象模式而不是类模式:)

呵呵~然后再说重要问题吧
因为我觉得在你的描述中总是出现链表,那么这个大家都应该清楚了:)
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产生的事件,呵呵:)

Collins

unread,
Mar 6, 2006, 8:34:58 AM3/6/06
to 高性能网络编程邮件列表
我的项目有单独的聊天服务器,目的是把场景相关(如战斗)和场景无关(如聊天)的功能隔离。
从玩家ID找到session恐怕只能完全搜索了,除非你的ID就是socket,也就是玩家上线后自动给他分配一个本次会话才有效的ID。可惜我的项目没法这么作,我的ID是unsigned
__int64的。

Collins

unread,
Mar 6, 2006, 8:59:11 AM3/6/06
to 高性能网络编程邮件列表
to 疯子阿虹:

链表的查询复杂度是o(n)

to all:

如果不能用数组存储session信息的话,
我是说用socket当下标,那就改用hash
map吧,性能紧次于数组,比map快。

sunway

unread,
Mar 6, 2006, 9:41:47 AM3/6/06
to 高性能网络编程邮件列表
未必,一般好的算法比如红黑数或者堆排序
,都是O(log N)
>>to 疯子阿虹:

>>链表的查询复杂度是o(n)

Ghost Cheng

unread,
Mar 6, 2006, 12:49:48 PM3/6/06
to dev4s...@googlegroups.com
Hi all:
 
多谢大家提供的建议,经过思考后,我修改了我的方案,
 
根据疯子阿虹说的,需要一个"能得到session并且算法复杂度为O(1)的操作"
我设计了一个session指针的数组,在初始化的时候创建,这个数组的大小目前定为10000000,至于为什么,下面说明。
 
根据yimlin说的"聊天和动作不是一个队列,至少是两个队列。好友上线可以利用数据库解决问题。聊天也不需要遍历整个玩家列表。完全可以利用聊天室房间的概念来控制。"
我也配合上面的指针数组,做了修改。
 
另外 to 疯子阿虹:
std::vector是O( 1 )
std::map是O( log2N )
std::list则是O( l )
 
这3个数据结构,只有list是插入、删除速度最快的,符合服务器需要,即使使用tree结构,可以优化查找,但是插入的速度却慢下来了。
所以,我保存玩家session的时候,仍然选择了list。但是这次,我不去做遍历链表的查询了。
 
下面详细说一下,还望大家指教,我本人是第一次做MMORPG,所以设计不当的地方,可能比较多,
也希望sunway 不要只是评论一下结果,最好能指出问题所在,并给出一个修正的建议方案、
 
好,废话不多说了,我修改后的构架是这样的,仍然分3个线程处理逻辑,
分别是:
1)事件处理线程,2)NPC动作计算线程,3)全局定时事件计时线程。
 
最主要的仍然是事件处理线程。
 
整个流程如下:
 
初始化的时候,逻辑层创建一个session指针的数组,大小是10000000,约占38MB的内存,(服务器内存不值钱,哈哈,以空间换时间)并memset为0。
为什么呢?因为我的角色ID,是使用数据库中自增字段的ID值的,全局唯一。
每个角色登陆上来后,就根据ID将对象指针放进这个数组对应的位置,这样就实现了,疯子阿虹说的,需要一个"能得到session并且算法复杂度为O(1)的操作"。
 
 并且,初始化的时候,还创建一个玩家链表,这个链表不是网络层的session链表,而是仅供事件处理线程使用的,所以不要加锁。
主要是为了发送全服通告使用。
另外每个场景地图对象上,也保存了一个玩家链表。每个坐标对象上,有一个角色指针。用于角色移动时的处理
 
网络层的N个线程,收到数据后,拼包,经过分析协议、解包后,得到完整的逻辑包,然后提交到逻辑包对应的处理函数。
这时逻辑包处理函数,分析,如果是玩家上线、下线、移动、攻击等操作,就提交对应的事件,到事件线程的处理队列。
 
 因为事件线程是单线程,所以玩家上线、下线,都会被这个单线程将角色的指针,插入上面说的玩家链表。 
 并且,将玩家的对象指针放进上面说的那个巨大的数组。
另外还需要将指针插入玩家所在地图的链表、以及将玩家所在坐标对象中的角色指针,设置为此玩家。
 
玩家移动的时候,根据所在地图上的玩家链表,查找周围的玩家,发送移动消息(这个地方,不知道有没有什么办法可以优化?)
如果坐标对象中的角色指针已经有数据了,说明这个坐标上有人,不可以移动。
 
好了,再来看下一个线程,NPC动作计算线程。
这个线程,主要负责计算NPC的AI,循环每个地图上的NPC。
例如:到了循环了某个NPC,发现它到了可以移动的时候,就提交一个NPC移动的事件到事件处理线程,
由事件处理线程广播给周围的玩家。
 
全局定时事件计时线程:这个线程,主要是计算全局的定时事件,例如玩家使用了某个道具,MAXHP在3分钟内增加到1000,
那么到了3分钟后,这个线程就提交一个事件给事件处理线程,通知它将该玩家的MAXHP降回原来的值。
 
那么如何聊天、以及好友上线通知呢?
这个因为有了上面创建的那个巨大的数组,聊天与好友,就根本不用提交到事件线程了。
直接在数据包处理函数中解决。因为那个数组只有事件处理线程有权写,所以别的线程只是读一下,不用加锁。
 
象这样,
收到玩家A的聊天数据,要转发给ID为999的玩家B,那么
LP_PLAYER lpPlayer = m_arrayPlayer[999];
if(lpPlayer)
{
   if(lpPlayer->socket == 有效)
      转发聊天内容;
}
else
{
  玩家B不在线,忽略或通知玩家A,发送失败。
}
 
好友上线通知,这次我改成了,不是由服务器主动通知玩家,而是玩家向服务器查询好友是否在线。
只要m_arrayPlayer[999] != NULL,那就表示在线。
 
 
以上是我目前的方案,还希望大家能讨论一下不足的地方。
 


--

Sincerely,

疯子阿虹

unread,
Mar 6, 2006, 8:30:29 PM3/6/06
to 高性能网络编程邮件列表
To Collins
链表的查询复杂度是o(n)
没有问题啊,
只不过我用的是小写的L,可能和1有点像:)

我觉得你单独有一个聊天服务器的话也可以,哪怕是线性搜索,也就无所谓了:)

STL的Map已经很快了。
他就使用红黑树实现的阿

疯子阿虹

unread,
Mar 6, 2006, 8:35:34 PM3/6/06
to 高性能网络编程邮件列表
To Ghost Cheng -

首先根本不用数组的实现为10000000,没有任何意义。
你把他定义到8192都已经很不错了:)
说一个很实际的问题,我以前的一个公司在2003年的一个成功运营的MMORPG项目中,这个值是800。。。。

其次你说的

这3个数据结构,只有list是插入、删除速度最快的,符合服务器需要,即使使用tree结构,可以优化查找,但是插入的速度却慢下来了。


我不这么认为,我认为,vector的插入,删除速度是最快的。
至少他会比链表少几个赋值操作:)

疯子阿虹

unread,
Mar 6, 2006, 8:39:56 PM3/6/06
to 高性能网络编程邮件列表
发件人: Collins - 查看个人资料
日期: 2006年3月6日(星期一) 下午9时34分
电子邮件: "Collins" <z...@zuo.com.cn>
Not yet rated评级:
显示选项


回复 | 答复作者 | 转发 | 打印 | 显示个别帖子 |
显示原始邮件 | 报告滥用行为 | 查找此作者的帖子


我的项目有单独的聊天服务器,目的是把场景相关(如战斗)和场景无关(如聊天)的功能隔离。

从玩家ID找到session恐怕只能完全搜索了,除非你的ID就是socket,也就是玩家上线后自动给他分配一个本次会话才有效的ID。可惜我的项目没法这­么作,我的ID是unsigned

__int64的。


------------------------
对于你的这段话我有一点不明白,动态的分配一个会话ID,当然是可以的。
和具体项目是什么应该没有关系吧。

Ghost Cheng

unread,
Mar 6, 2006, 8:42:14 PM3/6/06
to dev4s...@googlegroups.com
Hi 疯子阿虹:
 
我设置这个数组为10000000的大小,是因为我使用数据库中自增ID字段的值,作为下标,
这个值随着人物的注册增长,会一直增长下去.
 
另外,我不认同你说"vector的插入,删除速度是最快的",
按照Bjarne Stroustrup所著的<The C++ Programming Language> 中对STL几种容器的介绍.
vector的插入与删除速度是最慢的,仅仅是查找的时候很快,
因为vector是动态分配空间的,如果连续的空间不够了,就需要重新申请一段连续的空间,并且需要将原先的数据copy过来.
 


 
On 3/7/06, 疯子阿虹 <madma...@163.com> wrote:
To Ghost Cheng -

首先根本不用数组的实现为10000000,没有任何意义。
你把他定义到8192都已经很不错了:)
说一个很实际的问题,我以前的一个公司在2003年的一个成功运营的MMORPG项目中,这个值是800。。。。


yimlin

unread,
Mar 6, 2006, 8:45:30 PM3/6/06
to 高性能网络编程邮件列表
关于聊天设置的问题
“收到玩家A的聊天数据,要转发给ID为999的玩家B”那么潜在的含义是聊天是一对一聊。
但是在MMORPG中组队聊天是很正常的一个功能。换句话说。现有的设计不支持组队聊天。
建议考虑一下,单独建立一个聊天服务器,利用聊天室房间的概念,在服务器上保存组队成员信息:包括指针信息,和聊天记录时间。

yimlin

unread,
Mar 6, 2006, 8:47:30 PM3/6/06
to 高性能网络编程邮件列表
移动的消息,在很多游戏里采用了tile的概念,我不知道,你在游戏中是如何控制的。

Ghost Cheng

unread,
Mar 6, 2006, 8:52:09 PM3/6/06
to dev4s...@googlegroups.com
Hi yimlin:
 
接受建议,呵呵
其实这个问题,我一直在考虑,因为如果单独使用聊天服务器的话,那么玩家直接公开的聊天内容就会存在一个范围的问题.
因为玩家的公开聊天内容,只有周围的玩家才能看见,所以这中方式的聊天,需要判断周围玩家的位置.
如果使用聊天服务器,那么.
要么把这个功能,脱离聊天服务器,仍然靠游戏服务器转发,
要么就必须让游戏服务器实时通知聊天服务器,每个玩家的坐标
 
这个问题,我正在考虑中.


 
On 3/7/06, yimlin <yim...@gmail.com> wrote:
关于聊天设置的问题
"收到玩家A的聊天数据,要转发给ID为999的玩家B"那么潜在的含义是聊天是一对一聊。
但是在MMORPG中组队聊天是很正常的一个功能。换句话说。现有的设计不支持组队聊天。

Ghost Cheng

unread,
Mar 6, 2006, 8:56:21 PM3/6/06
to dev4s...@googlegroups.com
Hi yimlin:
 
我上面的方案中说了,关于移动,我采用链表和tile两种方式,
tile的方式仅仅是通知NPC,这个坐标上有没有玩家,
因为NPC作随机寻路的时候,本身就是要搜索周围的tile的.
 
但是玩家之间的移动同步,我使用链表的方式,转发消息的时候,在当前地图的玩家链表上查找周围的玩家.
因为搜索tile的范围会比较大,按照我的项目,搜索tile,那么每个移动消息,都要循环搜索300个tile.
如果地图人本身就不多,或人不是很集中,这个循环搜索tile就有点浪费了.


 
On 3/7/06, Ghost Cheng <ghost...@gmail.com> wrote:
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

疯子阿虹

unread,
Mar 6, 2006, 8:57:10 PM3/6/06
to 高性能网络编程邮件列表
使用数据库中的ID作为下标是一个很不成熟的做法。
而动态分配才是比较好的解决方案,当然,我无权干涉你的项目:)

我不明白为什么提到删除,你的意思就是把他从列队中去掉?
对于服务器来说,很多时候都是要自定义一些数据结构的。

举个简单的例子
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;
}

当然这仅仅是一个随手写的大概思想,真正的要比这个复杂一些:(

yimlin

unread,
Mar 6, 2006, 8:58:19 PM3/6/06
to 高性能网络编程邮件列表
如果结合tile的概念,那么你就可以吧这个问题简化,把其中一部分计算功能交给client端来处理。

yimlin

unread,
Mar 6, 2006, 9:00:21 PM3/6/06
to 高性能网络编程邮件列表
不好意思,没有看仔细了!呵呵!

Ghost Cheng

unread,
Mar 6, 2006, 9:01:37 PM3/6/06
to dev4s...@googlegroups.com
Hi yimlin:
我觉得,client是不能相信的,尤其是坐标计算方面的问题,
我目前的client,仅仅想服务器发送需要进行什么操作,而操作的参数,都是服务器自己计算.


 
On 3/7/06, yimlin <yim...@gmail.com> wrote:



Ghost Cheng

unread,
Mar 6, 2006, 9:06:03 PM3/6/06
to dev4s...@googlegroups.com
Hi 疯子阿虹:
 
我昨天考虑过你的动态ID的办法,觉得存在一个问题,
A玩家肯定要获得其他玩家的动态ID,才能对服务器发送指令,
那么如果某个玩家下线了,他原先动态ID,被分配给了另一个玩家,
而A玩家并不知道ID的主人已经变了,他仍然对服务器说"发送聊天给这个ID"
那么服务器不就转发错误的ID了么?
如果依靠数据包中除了ID,还包含人物的名字,来判断ID是否正确.
我觉得数据包会比较大呀.
尤其是其他的一些操作,本来只要2-4个字节的包就能解决的,如果带上名字,就要10-20个字节了.

 
On 3/7/06, Ghost Cheng <ghost...@gmail.com> wrote:

疯子阿虹

unread,
Mar 6, 2006, 9:15:40 PM3/6/06
to 高性能网络编程邮件列表
看了你这两篇帖子,有几个问题需要告诉你。
第一个你所谓不相信客户端的做法,我觉得大可不必,因为无论如何,外挂都是可以出现的,除非你是在大公司,能有很强的力度来进行外挂的打击,否则,有些东西不妨可以相信客户端,哪怕是有一些错误,看看魔兽世界你就知道了。


第二个,这就要看你怎么管理你的动态ID了,也就是服务器程序员的工作了,否则服务器程序员岂不是很轻松:)因为在我的感觉下,服务器程序员远远比客户端程序员轻松,也深有体会:)当然服务器逻辑程序员事情比较多一些:)

最后一个就是,我觉得没有必要在乎那几个字节,据我以前的项目观察,流量好像都不是什么问题,你需要减少的是通讯的次数,因为socket是I/O操作,一般来说,效率绝对不是在你多了那么几个字节上:)

当然,因为服务器设计比较复杂,很可能我的观点也是错误的。
即供参考:)

Ghost Cheng

unread,
Mar 6, 2006, 9:20:53 PM3/6/06
to dev4s...@googlegroups.com
Hi 疯子阿虹:
 
哈~多谢你的建议~
"服务器程序员远远比客户端程序员轻松",这句话我倒很是赞同,
我现在即做服务器,又写客户端,明显感觉客户端的代码写起来比较累.


 
On 3/7/06, 疯子阿虹 <madma...@163.com> wrote:

Collins

unread,
Mar 8, 2006, 12:16:29 AM3/8/06
to 高性能网络编程邮件列表
to Ghost Cheng:

> 其实这个问题,我一直在考虑,因为如果单独使用聊天服务器的话,那么玩家直接公开的聊天内容就会存在一个范围的问题.
> 因为玩家的公开聊天内容,只有周围的玩家才能看见,所以这中方式的聊天,需要判断周围玩家的位置.
> 如果使用聊天服务器,那么.
> 要么把这个功能,脱离聊天服务器,仍然靠游戏服务器转发,
> 要么就必须让游戏服务器实时通知聊天服务器,每个玩家的坐标
>
> 这个问题,我正在考虑中.


呵呵,你说的没错,聊天服务器应该改成"场景无关聊天服务器"或者"玩家关系服务器"更合适,它的作用就是处理那些和场景无关的功能,普通聊天(就是你说的只有周围人才能看到的),我们是放在场景服务器处理的。

放在"场景无关聊天服务器"的功能还有私聊、组队聊天、公会聊天、自建聊天室聊天等等,这些都是和场景无关的,但是信息量却非常大,如果放到场景服务器的话,玩家较多的时候,会严重影响高优先级的消息发送。

其实放在一个服务器也可以,那你的发送队列就要支持优先级,但是我不建议使用
stl 的
priority_queue,那样你在发送完高优先级的包之前就不能发送聊天消息了。比较理想的方式是按一定的比例发送,这个可能需要自己写一个了。

good luck.

Reply all
Reply to author
Forward
0 new messages