從前有個國王叫做混沌
他沒有七竅
沒辦法與外界交流
兩個朋友希望他開心 就給他鑿了七竅
於是他就死了。
所以我們這一章來給用戶添加七竅,讓用戶和房間具有最 基本的聊天功能。
什麼?我前面的故事什麼意思?
我也不知道。
其實,網絡游戲 交流的最基礎,就是聊天室。
如果我們把任何一個網絡游戲高度抽象化,把所有的非共性的部分 全部去掉,我們會得到這樣一個抽象的流程。
客戶端發包 給服務器。
服務器處理包。
客戶端收取廣播數據。
剛剛好 聊天室恰恰實現了這樣一個最簡化的流程。
用戶 說話。
服務器作簡單的轉向處理。
客戶端收取廣播數據。
一些經過擴展的聊天室 甚至已經具有了游戲的雛形。
經過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
'-----------------------------
' Wayne Wang
' 個人研究
' 不是什麼了不起的東西
' 有錯誤的話還請告訴我
' 努力奮斗
' Yeah!
' -----------------------------
Namespace CommonNamespace Common
Public Interface IServiceInterface IService
ReadOnly Property PublicChannels() Property PublicChannels() As IDictionary(Of String, IChatChannel)
ReadOnly Property Areas()Property Areas() As IDictionary(Of String, IArea)
End Interface
End Namespace
IChatChannel
Code
'--------------------- --------
' Wayne Wang
' 個人研究
' 不是什麼 了不起的東西
' 有錯誤的話還請告訴我
' 努力奮斗
' Yeah!
'-----------------------------
Namespace CommonNamespace Common
/**/''' <summary>
''' 消息頻道
''' </summary>
Public Interface IChatChannelInterface IChatChannel
/**/''' <summary>
''' 消息內容
''' </summary>
ReadOnly Property MessageLinkList()Property MessageLinkList() As LinkedList(Of IChatMessage)
/**/''' <summary>
''' 消息過期時間
''' </summary>
Property TimeoutSecond()Property TimeoutSecond() As Integer
/**/''' <summary>
''' 本頻道中 某個用戶收到的最後一個節點
''' </summary>
ReadOnly Property LastReceivedMessage()Property LastReceivedMessage() As Generic.IDictionary(Of String, LinkedListNode(Of IChatMessage))
/**/''' <summary>
''' 本頻道的類別
''' </summary>
ReadOnly Property Type()Property Type() As ChannelType
/**/''' <summary>
''' 頻道顯示名
''' </summary>
ReadOnly Property Name()Property Name() As String
/**/''' <summary>
''' 頻道唯一ID
''' </summary>
ReadOnly Property ID()Property ID() As String
/**/''' <summary>
''' 清除用戶的最後收到信息
''' </summary>
Sub Clear()Sub Clear()
/**/''' <summary>
''' 發送信息給本頻道 根 據信息的類型自動匹配發送的方式
''' </summary>
Sub Send()Sub Send(ByVal message As Common.IChatChannel)
/**/''' <summary>
''' channel的類別
''' </summary>
Enum ChannelTypeEnum ChannelType
/**/''' <summary>
''' 公共頻道
''' </summary>
PublicChannel = 0
/**/''' <summary>
''' 本地頻道
''' </summary>
AreaChannel = 1
/**/''' <summary>
''' 工會頻道
''' </summary>
GuildChannel = 2
/**/''' <summary>
''' 臨時頻道
''' </summary>
TemporyChannel = 3
/**/''' <summary>
''' 小隊頻道
''' </summary>
TeamChannel = 4
End Enum
End Interface
End Namespace
IChater
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 String, ByVal 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 String, ByVal 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 String, ByVal 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 String, ByVal 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
Code
'-----------------------------
' Wayne Wang
' 個人研究
' 不是什麼了不起的 東西
' 有錯誤的話還請告訴我
' 努力奮斗
' Yeah!
'-----------------------------
Namespace CommonNamespace Common
Public Interface IChatroomInterface IChatroom
Inherits IArea
/**/''' <summary>
''' 本區域的有效聊天頻道
''' </summary>
ReadOnly Property LocalChannels() Property LocalChannels() As IDictionary(Of String, Common.IChatChannel)
/**/''' <summary>
''' 本區域的缺省聊天頻道
''' </summary>
ReadOnly Property DefaultLocalChannel()Property DefaultLocalChannel() As Common.IChatChannel
/**/''' <summary>
''' 本區域繼承的頻道
''' </summary>
ReadOnly Property MountedChannels() Property MountedChannels() As IDictionary(Of String, IChatChannel)
/**/''' <summary>
''' 本區域繼承的聊天頻道中的 公共頻道
''' </summary>
ReadOnly Property PublicChannels()Property PublicChannels() As IChatChannel
End Interface
End Namespace