游戏大厅 从基础开始(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

 

ContractedBlock.gif ExpandedBlockStart.gif Code

'-----------------------------
'
         Wayne Wang 
'
          个人研究 
'
    不是什么了不起的东西 
'
    有错误的话还请告诉我
'
          努力奋斗
'
            Yeah!
'
-----------------------------



ExpandedBlockStart.gifContractedBlock.gif
Namespace CommonNamespace Common

ExpandedSubBlockStart.gifContractedSubBlock.gif    
Public Interface IServiceInterface IService
ExpandedSubBlockStart.gifContractedSubBlock.gif        
ReadOnly Property PublicChannels()Property PublicChannels() As IDictionary(Of String, IChatChannel)

ExpandedSubBlockStart.gifContractedSubBlock.gif        
ReadOnly Property Areas()Property Areas() As IDictionary(Of String, IArea)

    
End Interface


End Namespace

 

IChatChannel

 

 

ContractedBlock.gif ExpandedBlockStart.gif Code

'-----------------------------
'
         Wayne Wang 
'
          个人研究 
'
    不是什么了不起的东西 
'
    有错误的话还请告诉我
'
          努力奋斗
'
            Yeah!
'
-----------------------------



ExpandedBlockStart.gifContractedBlock.gif
Namespace CommonNamespace Common

ExpandedSubBlockStart.gifContractedSubBlock.gif    
/**/''' <summary>
    
''' 消息频道
    
''' </summary>

ExpandedSubBlockStart.gifContractedSubBlock.gif    Public Interface IChatChannelInterface IChatChannel

ExpandedSubBlockStart.gifContractedSubBlock.gif        
/**/''' <summary>
        
''' 消息内容
        
''' </summary>

ExpandedSubBlockStart.gifContractedSubBlock.gif        ReadOnly Property MessageLinkList()Property MessageLinkList() As LinkedList(Of IChatMessage)

ExpandedSubBlockStart.gifContractedSubBlock.gif        
/**/''' <summary>
        
''' 消息过期时间
        
''' </summary>

ExpandedSubBlockStart.gifContractedSubBlock.gif        Property TimeoutSecond()Property TimeoutSecond() As Integer



ExpandedSubBlockStart.gifContractedSubBlock.gif        
/**/''' <summary>
        
''' 本频道中  某个用户收到的最后一个节点
        
''' </summary>

ExpandedSubBlockStart.gifContractedSubBlock.gif        ReadOnly Property LastReceivedMessage()Property LastReceivedMessage() As Generic.IDictionary(Of String, LinkedListNode(Of IChatMessage))

ExpandedSubBlockStart.gifContractedSubBlock.gif        
/**/''' <summary>
        
''' 本频道的类别
        
''' </summary>

ExpandedSubBlockStart.gifContractedSubBlock.gif        ReadOnly Property Type()Property Type() As ChannelType

ExpandedSubBlockStart.gifContractedSubBlock.gif        
/**/''' <summary>
        
''' 频道显示名
        
''' </summary>

ExpandedSubBlockStart.gifContractedSubBlock.gif        ReadOnly Property Name()Property Name() As String

ExpandedSubBlockStart.gifContractedSubBlock.gif        
/**/''' <summary>
        
''' 频道唯一ID
        
''' </summary>

ExpandedSubBlockStart.gifContractedSubBlock.gif        ReadOnly Property ID()Property ID() As String





ExpandedSubBlockStart.gifContractedSubBlock.gif        
/**/''' <summary>
        
''' 清除用户的最后收到信息
        
''' </summary>

ExpandedSubBlockStart.gifContractedSubBlock.gif        Sub Clear()Sub Clear()


ExpandedSubBlockStart.gifContractedSubBlock.gif        
/**/''' <summary>
        
''' 发送信息给本频道  根据信息的类型自动匹配发送的方式
        
''' </summary>

ExpandedSubBlockStart.gifContractedSubBlock.gif        Sub Send()Sub Send(ByVal message As Common.IChatChannel)




ExpandedSubBlockStart.gifContractedSubBlock.gif        
/**/''' <summary>
        
''' channel的类别
        
''' </summary>

ExpandedSubBlockStart.gifContractedSubBlock.gif        Enum ChannelTypeEnum ChannelType
ExpandedSubBlockStart.gifContractedSubBlock.gif            
/**/''' <summary>
            
''' 公共频道
            
''' </summary>

            PublicChannel = 0
ExpandedSubBlockStart.gifContractedSubBlock.gif            
/**/''' <summary>
            
''' 本地频道
            
''' </summary>

            AreaChannel = 1
ExpandedSubBlockStart.gifContractedSubBlock.gif            
/**/''' <summary>
            
''' 工会频道
            
''' </summary>

            GuildChannel = 2
ExpandedSubBlockStart.gifContractedSubBlock.gif            
/**/''' <summary>
            
''' 临时频道
            
''' </summary>

            TemporyChannel = 3
ExpandedSubBlockStart.gifContractedSubBlock.gif            
/**/''' <summary>
            
''' 小队频道
            
''' </summary>

            TeamChannel = 4
        
End Enum



    
End Interface

End Namespace

 

 

 

IChater

 

ContractedBlock.gif ExpandedBlockStart.gif Code
 1 
 2 '-----------------------------
 3 '         Wayne Wang 
 4 '          个人研究 
 5 '    不是什么了不起的东西 
 6 '    有错误的话还请告诉我
 7 '          努力奋斗
 8 '            Yeah!
 9 '-----------------------------
10 
11 
12 Namespace Common
13 
14     Public Interface IChater
15         ''' <summary>
16         ''' 聊天者的唯一ID
17         ''' </summary>
18         ReadOnly Property ChaterID() As String
19         ''' <summary>
20         ''' 除了区域和公共频道 额外加入的频道  比如工会、小队、自定义频道
21         ''' </summary>
22         ReadOnly Property ExtraMountedChannels() As IDictionary(Of String, Common.IChatChannel)
23 
24         ''' <summary>
25         ''' 被屏蔽的频道列表
26         ''' </summary>
27         Property MutedChannelNameList() As IEnumerable(Of String)
28 
29         ''' <summary>
30         ''' 被屏蔽的用户列表
31         ''' </summary>
32         Property MutedUIDList() As System.Collections.Generic.IEnumerable(Of String)
33 
34 
35 
36         ''' <summary>
37         ''' 对目标说话
38         ''' </summary>
39         ''' <param name="message">信息文本</param>
40         ''' <param name="toChater">目标用户</param>
41         Sub Say(ByVal message As StringByVal toChater As IChater)
42 
43         ''' <summary>
44         ''' 在当前位置公开说话
45         ''' </summary>
46         Sub Say(ByVal message As String)
47 
48         ''' <summary>
49         ''' 在当前位置公开发送信息
50         ''' </summary>
51         ''' <param name="message">信息文本</param>
52         ''' <param name="type">信息类型</param>
53         Sub Say(ByVal message As StringByVal type As Common.IChatMessage.MessageRenderType)
54 
55         ''' <summary>
56         ''' 对目标发送信息
57         ''' </summary>
58         ''' <param name="message">信息文本</param>
59         ''' <param name="toChater">目标用户</param>
60         ''' <param name="type">信息类型</param>
61         Sub Say(ByVal message As StringByVal toChater As IChater, ByVal type As Common.IChatMessage.MessageRenderType)
62 
63         ''' <summary>
64         ''' 详细的发送信息
65         ''' </summary>
66         ''' <param name="message">信息文本</param>
67         ''' <param name="toChater">目标用户</param>
68         ''' <param name="type">消息类型</param>
69         ''' <param name="channel">发送消息的频道</param>
70         Sub Say(ByVal message As StringByVal toChater As IChater, ByVal type As Common.IChatMessage.MessageRenderType, ByVal channel As Common.IChatChannel)
71 
72         ''' <summary>
73         ''' 接收聊天信息
74         ''' </summary>
75         Function Listen() As IEnumerable(Of IChatMessage)
76 
77 
78 
79 
80 
81 
82 
83 
84 
85 
86     End Interface
87 
88 End Namespace

 

 

 

 IChatroom

 

ContractedBlock.gif ExpandedBlockStart.gif Code

'-----------------------------
'
         Wayne Wang 
'
          个人研究 
'
    不是什么了不起的东西 
'
    有错误的话还请告诉我
'
          努力奋斗
'
            Yeah!
'
-----------------------------



ExpandedBlockStart.gifContractedBlock.gif
Namespace CommonNamespace Common
ExpandedSubBlockStart.gifContractedSubBlock.gif    
Public Interface IChatroomInterface IChatroom
        
Inherits IArea



ExpandedSubBlockStart.gifContractedSubBlock.gif        
/**/''' <summary>
        
''' 本区域的有效聊天频道
        
''' </summary>

ExpandedSubBlockStart.gifContractedSubBlock.gif        ReadOnly Property LocalChannels()Property LocalChannels() As IDictionary(Of String, Common.IChatChannel)



ExpandedSubBlockStart.gifContractedSubBlock.gif        
/**/''' <summary>
        
''' 本区域的缺省聊天频道
        
''' </summary>

ExpandedSubBlockStart.gifContractedSubBlock.gif        ReadOnly Property DefaultLocalChannel()Property DefaultLocalChannel() As Common.IChatChannel


ExpandedSubBlockStart.gifContractedSubBlock.gif        
/**/''' <summary>
        
''' 本区域继承的频道
        
''' </summary>

ExpandedSubBlockStart.gifContractedSubBlock.gif        ReadOnly Property MountedChannels()Property MountedChannels() As IDictionary(Of String, IChatChannel)



ExpandedSubBlockStart.gifContractedSubBlock.gif        
/**/''' <summary>
        
''' 本区域继承的聊天频道中的公共频道
        
''' </summary>

ExpandedSubBlockStart.gifContractedSubBlock.gif        ReadOnly Property PublicChannels()Property PublicChannels() As IChatChannel


    
End Interface

End Namespace

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值