游戏大厅 从基础开始(2)——最基础的交流:聊天

游戏大厅 从基础开始(2)

——最基础的交流:聊天

从前有个国王叫做混沌

他没有七窍

没办法与外界交流

两个朋友希望他开心 就给他凿了七窍

于是他就死了。

 

所以我们这一章来给用户添加七窍,让用户和房间具有最基本的聊天功能。

 

什么?我前面的故事什么意思?

。。。。。。。

。。。。。

。。。。

。。。

。。

 

我也不知道。

 

 

 

 

 

 

其实,网络游戏交流的最基础,就是聊天室。

如果我们把任何一个网络游戏高度抽象化,把所有的非共性的部分全部去掉,我们会得到这样一个抽象的流程。

  1. 客户端发包 给服务器。
  2. 服务器处理包。
  3. 客户端收取广播数据。

 

刚刚好 聊天室恰恰实现了这样一个最简化的流程。

  1. 用户说话。
  2. 服务器作简单的转向处理。
  3. 客户端收取广播数据。

 

一些经过扩展的聊天室 甚至已经具有了游戏的雏形。

经过1999年互联网泡沫年代的人,应该还记得有奖抢答的聊天室。

聊天室会定期向用户发送问题,用户根据特殊的命令向服务器发送答案 在指定的时间内完成的,就可以得到相应的经验奖励。

没错这就是传奇的抢怪。

实际上最早的文字mud也是建立在irc协议的聊天室上。

甚至我们现实世界的游戏 也是一群想喝酒聊天而苦于没什么话题的人,在聊天的基础上建立的。

 

我们可以从结构里、从历史上、从YY中得出以下一系列引申结论。

  • 网路游戏的操作命令就是特殊的聊天内容。
  • 网络游戏的返回结果就是特殊的聊天结果广播
  • 网络游戏,就是一个个特殊的聊天室。

 

也就是说 我们完成一个全功能聊天室的时候,我们已经完成了一个游戏的80%了

那么这个结论对我们今天的主题有什么关系呢?

。。。。。。。

。。。。。

。。。。

。。。

。。

 

有感而发而已,一毛钱关系都没有。

 

 

 

 

首先我们来看看

聊天聊的是什么

聊天信息

 

这个Interface 声明了一个聊天信息必要的所有特性。详细内容可以参考代码注释

 

 

 

聊天频道

定义

频道:聊天者发言的存贮转发器

 

 

 

在这里 我设计的频道把用户发出的信息存储在一个链表中。

为什么我们要把他们存储在频道中呢?

其实并不是每一个通道都很清楚自己的成员在哪里。

如果一个用户进出一个区域都要到对应的频道签个到 告个假 产生的系统消耗是巨大的。

作为一个区域性质的大频道 你总不能每得到一个包 都要这个频道到每个子区域去穷举用户吧,那可真的得不偿失

 

所以我们把每个频道中的信息按照顺序排好,所有用户都可以把自己最后一次读取的节点当成凭据 得到所有和自己相关 未读取过的信息。

 

 

 

 

聊天者 IChater 聊天的人

  • 能够发出信息(其实是一个带Ichatmessage工厂的投递器)
  • 能够接收所有相干的信息
  • 能够过滤掉自己无关的和有意识屏蔽的人和频道

 

 

(这里要特别声明一下 IChater 没有继承IUser 是因为以后可能增加聊天机器人这样的扩展Chater 不过这样的chater如何工作,是否要继承IUser还是单独建立INPC还没有考虑清楚,BS我吧。)

 

 

 

 

聊天室 IChatroom 能聊天的区域

实现本地频道

    

 

 

 

 

 

频道和 用户/区域间的架构

 

 

补充定义:

地区频道:成员为本区域聊天成员的频道

公共频道:一个大范围内多区域共享的频道,一般是服务级别/服务器级别的聊天频道

继承频道: 一个区域从他的相关频道"学习"到的频道。包括从服务中继承的公共频道 或者从父区域继承的区域广播。

临时频道:用户自己建立的频道 如果长时间没有人访问则自动销毁,如果有人访问则重新建立的随机频道

组队频道:特殊的临时频道,随着组队的存在而存在。

 

考虑一下目前最复杂最华丽的mmo角色扮演聊天室,魔兽世界的功能现状,我们作出如下需求分析.

 

  • 每个区域都有相应的若干地域性的聊天频道。
  • 每个服务器都有相应的若干公共频道 传递系统公告和密语。
  • 每个工会都有自己的频道
  • 每个小队都有自己的临时频道
  • 每个用户都可以创建自己的临时频道

 

这就麻烦了,如何定义channel在各个对象中的布局 才能满足如此变态需求呢?

 

我们给最上层的服务 增加一个publicchannels 频道集合 来处理公告/交易/密语 因为他们是全服务器范围的 (甚至跨服务器范围的)

 

 

 

 

 

在这个服务内所有的区域 在创建的时候 都把这些公共频道引用当成自己的初始成员。对于有父区域向子区域广播的类型 我们也要把父区域的channel加入这个集合

这就好像是事件监听机制一样。这表示'这些频道 我这个区域的所有聊天者参与监听了'

 

 

 

 

同样的,我们对聊天者也可以有同样的处理 可以把临时频道、工会频道、小队频道 注册到用户监听频道列表中。

 

 

 

这样我们在Ichater.listen()中 只需要对自己身边几个重要集合中的IChatchannel 进行检查 就可以收集到一切自己没有听到的 与自己相关的消息。

 

 

整体图如下

 

 

 

 

我们在实现IChatchannel的时候 可以用代理模式做成远程web service的代理

这样就可以实现部分负载分离,listen()的时候偶尔读取一次罢了

如果客户端的Ichater是可以直接连接到远程聊天服务进程的另一个代理(比如udp协议代理),那么整个聊天流程就完整地脱离大厅了。

在聊天流量巨大,已经喧宾夺主的情况下 这种分离不失为一种选择

当然这是后话。

 

 

参考代码

 

IService

 

Code

 

IChatChannel

 

 

Code

 

 

 

IChater

 

Code

 

 

 

 IChatroom

 

Code
posted @ 2008-12-17 01:08  MSFT:waywa 韦恩卑鄙  阅读(4026)  评论(20编辑  收藏  举报