Guild
第 6 章 公會處理
6-1 認識公會(Guild)
公會是遊戲中的玩家群組,可讓玩家相互連繫與交流,和場景、物品、積分板不同的是,公會並沒有類別(Class)與實例(Instance) 的物件架構,所以不像場景、物品、積分板需要後台管理類別庫,但是仍需要管理與安全有關的 api 執行權限。
公會是凝聚玩家黏著遊戲的重要工具,有些特殊的遊戲玩法都得加入公會才能進行,SGC 的公會 api 著重在公會與成員的管理,開發者可以善用公會及會員資料中的暫存器與 tag
屬性,自行定義這些資料的對玩家的意義,藉以開啟、關閉某些遊戲功能,或記錄這些功能的屬性值等等。
公會成員角色
玩家在公會可能擔任的角色有三種,角色和 API 的執行權限有關,稍後章節會有詳細的說明。
公會處理程式
公會處理的 API 函式都在 CloudGuild 類別中,此類別是由靜態函式組成,沒有建構子(Constructor), 也沒有物件屬性(Property),程式設計師要進行與物品相關的處理,請直接叫用這些函式。
接下來所介紹的 API 函式,以下一節的 CreateGuild()
為例,程式設計師呼叫時要在前面加上「CloudGuild.」,就像這個樣子:CloudGuild.CreatGuild()。
6-2 建立公會與刪除公會
要建立公會,必須在遊戲程式中呼叫 CreateGuild() 函數,函數定義如下:
csharp
public static void CreateGuild(CloudGame game, string name, string note, int maxmember, int permission, int tag,
int G1, int G2, int G3, int G4, OnCallCompletionWithData cb, object token)
(1) name
所建立的公會之名稱,開發者若因遊戲企劃而要限制公會名稱的話,請自行設計過濾的名稱的使用者介面,此參數並無內容限制。
(2) note
所建立的公會之描述說明文字。
(3) maxmember
用來指定可加入此公會的最大會員數(最大值為100)。
(4) permission
用來指定何人才能加入公會,0: 任何人都可以加入公會,1: 公會管理員或超級使用者才有權限決定加玩家能否加入公會。
(5) tag
公會標籤。
(6) G1~G4
用來指定公會的暫存器值,暫存器在遊戲中所代表的意思可由開發者彈性規劃。
cb 為完成 API 後的 callback 函式,其函式定義如下:
void cb(int code, object obj, object token)
code 0 成功。 非 0 失敗,code為錯誤碼。
obj 當code = 0 時,(uint) obj 存放公會的識別碼。 當code 非 0 時,obj 則無意義,可以略過。
公會建立之後,呼叫者就是公會的會長,會長具有更改公會基本資料、刪除公會,管理公會會員的權限。玩家如果已經是其它公會的成員,無論是一般會員、管理員或是會長,他就不能再創建新的公會。
如果會長不想要讓公會繼續存在,可以呼叫 DeleteGuild ()
函數,函數定義如下:
public static void DeleteGuild(CloudGame game, OnCallCompletion cb, object token)
刪除公會成功後,callback 的 code
參數為 0,反之,失敗則為非的錯誤碼 0。
執行權限
這個函式只能被會長呼叫,其他管理員和一般玩家呼叫,callback 的 code 參數則會傳會權限錯誤的錯誤碼。
如果開發者決定建立與刪除公會必須在伺服器邏輯中進行,那麼 DP 程式就必須呼叫suCreateGuild()
與 suDeleteGuild()
,呼叫時指定 guildid 公會識別碼,否則 callback 將會傳回權限錯誤。
更改公會資料
公會建立後,只有會長可以修改公會資料,例如公會名稱、公會描述、公會的開放性等等,在實務上,開發者必須在遊戲程式製作 UI 讓玩家(公會會長)輸入要更新的資料,然候呼叫UpdateGuild()
進行資料更新。
不過開發者得注意一件事,為了維持遊戲的安全與公平,有些公會資料與遊戲玩家間競爭有關,例如用來記錄公會等級、成就等資料的公會暫存器,最好不要由遊戲用戶端程式直接更改,而是透過 DP 伺服器邏輯,以suUpdateGuild()
更改,以免讓外掛程式有機可乘。
6-3 列舉已建立的公會資料
遊戲程式一開始總要知道目前已經建立的公會有那些,這裡有幾個不同的列舉函式,可以依照不同的搜尋需要,列舉出所遊戲玩家所要看到的公會。
函式名稱 | 說明 |
---|---|
GetGuild | 一般性的搜尋列舉 |
GetGuildByName | 依照公會名稱搜尋。呼叫時所傳入的參數name 就是要搜尋的關鍵字。請注意,公會名稱與name 參數完全符合的公會資料,會放在傳回的List <Hashtable> 中的第一個元素,所以如果只要找名稱相符的,請設定參數maxmmber=1 |
GetGuildByPermission | 依照公會的開放程度搜尋,指定參數permssion 的值選擇所要列舉的類型: 0:任何人都可加入、 1:只有會長或管理員核准才能加入、2:任何人都無法加入。 |
GetGuildByTag | 依照公會的Tag 值搜尋 |
GetGuildByUserid | 依照玩家的userid ,找到他所屬的公會,由於玩家只能加入一個公會,所以此函式最多只會傳回最一筆公會資料。 |
公會資料
呼叫後,所列舉的公會資料是以List <Hashtable>
的方式透過 callback 函式傳回,List 中的每個 Hashtable 元素就是一個公會的資料,共有以下 Key 值,存放公會的資料:
Key | Value | Type |
---|---|---|
guildid | 公會識別碼 | int |
name | 公會名稱 | string |
note | 公會的描述說明 | string |
leader | 公會會長的 nickname | string |
maxnumber | 公會可加入的最大會員(玩家)數 | int |
playernumber | 目前公會已加入的會員(玩家)總數 | int |
tag | 公會標籤 | int |
permission | 公會開放程度 0: 任何人都可加入、 1: 只有會長或管理員核准才能加入、2: 任何人都無法加入。 | int |
g1 | 暫存器 G1,開發者自定屬性 | int |
g2 | 暫存器 G2,開發者自定屬性 | int |
g3 | 暫存器 G3,開發者自定屬性 | int |
g4 | 暫存器 G4,開發者自定屬性 | int |
g1stamp | 最近一次更新暫存器 G1 的時間 | string |
g2stamp | 最近一次更新暫存器 G2 的時間 | string |
g3stamp | 最近一次更新暫存器 G3 的時間 | string |
g4stamp | 最近一次更新暫存器 G4 的時間 | string |
createtime | 公會建立的時間 | string |
applystate | 呼叫者申請加入此公會的申請單狀態 0: 未申請 or 無資料 (由於申請單不會永久保留,刪掉後就會呈現無資料的狀態)、1: 公會管理員審核中、2: 已接受加入公會、3: 已被拒絕加入公會 | int |
排序方式
每個公會都有四個暫存器 G1、G2、G3、G4,可以存放 4 個整數資料。
暫存器的設計讓遊戲開發者可以彈性應用並自行定義他們的用途,在呼叫 GetGuild()
相關函式時,可依照暫存器值排列公會的順序。
舉例來說, 我們可以定義 G1 是公會等級,當玩家選擇公會時,遊戲程式要把等級高的放前面,在呼叫GetGuild()
時,指定參數 OrderBy
如下:
csharp
String OrderBy="g1 desc";
或是並用多個暫存器和公會建立時間做為排序條件,以「,」分開:
csharp
String OrderBy="g1 desc, g2 asc, createtime asc";
條件過濾
在搜尋列舉公會資料時,有時候會因為玩家等級,或是其它目的,會希望只列舉某些符合條件的工會,GetGuild()
可以在呼叫時,透過參數指定過濾條件。
過濾條件可以是暫存器值、公會建立時間、或是兩者一起。而暫存器過濾也可以是一個邏輯運算式, 例如 G1 是公會等級,G2 是公會戰鬥力,暫存器過濾可以這樣指定:
csharp
String RegisterCondition="2<g1<5 and 10>g2";
就可以列舉搜出等級介於 2 和 5 之間,戰鬥力小於 10 的公會。
其他參數
GetGuild()
相關函式的 Overloading 很多,依照不同的呼叫需要有各種參數,以下是所有參數的說明:
orderby
排序控制字串,可以指定用暫存器值、公會建立時間來排序, Keyword 有以下數種,另外可加後綴 asc 或 desc 表示升序或降序,沒有指定的話,則預設為 asc :
Keyword | 說明 |
---|---|
g1 | 依 G1 暫存器值排序 |
g2 | 依 G2 暫存器值排序 |
g3 | 依 G3 暫存器值排序 |
g4 | 依 G4 暫存器值排序 |
createtime | 依公會建立時間 |
例如: ”g1 desc
”
如果一次要指定多個排序條件,則請用「,」分隔, 例如: ”g1 asc,g3 desc,createtime desc
”
MaxNumber
指定讀取資料筆數上限。
請注意,GetGuildByName()
是依參數 name
做名稱的關鍵字搜尋,名稱完全符合的會排在第一筆,所以如果只要取得名稱完全符合公會,請設MaxNumber = 1
。
-
RegiterCondition
指定暫存器條件,傳回符合條件的公會列表,條件式是由「>」、「<」、「=」、「<=」、「>=」、「and」、「or」等組成, 例如: ” 2<g1<5 and 10>g2 or g3=5 and 1<g4 <50” -
Start
列舉公會建立時間晚於此時間者 -
End
列舉公會建立時間早於此時間者
6-4 公會成員管理
玩家在遊戲中要加入公會,可以將 UI 設計成下圖的樣子:
公會的資料則是從 GetGuild
相關函式取得,所傳回的公會 Hashtable 中的”applystate
” Key值若等於 0,就可顯示「我要加入」的按鈕;上圖的「已發送」,則是”applystate
” Key等於 1 的結果。玩家點選之後,遊戲程式要讓玩家加入公會的作法有兩種:
-
直接加入
如果公會的開放性是設定成任何人都可以加入,遊戲程式就可以直接呼叫JoinGuild()
讓玩家加入,參數guildid
值則是設定為要加入的公會之識別碼,此識別碼可由前一節所介紹的GetGuild()
列舉公會資料時取得。 -
核准加入
加入公會需到公會會長或管理員核准,遊戲程式就必須提供會員管理的子系統,此處有幾個 api 函式可以協助遊戲程式建立完整的公會申請加入與核准流程,除了前面所介紹,非會開發者搜尋公會的介面,選擇要加入的公會發出加入公會的申請要求後,公會管理員也需要向這樣的招收會員的介面,顯示要求加入公會的申請單:
此時,非會員的玩家也不能直接呼叫 api 加入公會,在 SGC 的權限控管下,只要公會建立時,會長指定開放性為 permission=1,就不必擔心玩家使用非法程式不經同意任意加入公會。
為了方便瞭解整個公會加入與核准流程,我們用下圖的步驟 1 到步驟 11 來說明,開發者可以依此將它們設計在遊戲程式內;最後的步驟 12 與步驟 13,則是在公會管理員核准或拒絕了某玩家的入會申請後,回歸到遊戲最基本的即時通訊系統 CloudScene、CloudGame,以場景或私訊通知玩家,或是使用 CloudMailbox 功能寄送離線訊息通知玩家。
列舉公會成員
在任何情況,要顯示公會中的成員,只要呼叫 GetGuildMemebr()
,callback 傳回 List Hashtable
, 每個 Hashtable 存放一個會員資料,Key/Value 如下表:
Key | Value | Type |
---|---|---|
guildid | 公會識別碼 | int |
userid | 玩家的 userid | String |
nickname | 玩家的暱稱 | String |
online | 0:玩家不在線上、非 0:玩家在目前在線上之 poid | uint |
privilege | 成員在此公會的權限 0: 一般會員、1:(特權會員)管理員、2:(特權會員)會長 | int |
r1 | 暫存器 R2,開發者自定屬性 | int |
r2 | 暫存器 R3,開發者自定屬性 | int |
r3 | 暫存器 R4,開發者自定屬性 | int |
r4 | 暫存器 R4,開發者自定屬性 | int |
r1stamp | 上次更改 R1 暫存器的時間 | String |
r2stamp | 上次更改 R2 暫存器的時間 | String |
r3stamp | 上次更改 R3 暫存器的時間 | String |
r4stamp | 上次更改 R4 暫存器的時間 | String |
jointime | 加入公會的時間 | String |
這個函式的 Overloading 也很多,依各個參數的有無來決定:
-
guildid
指定要讀取的公會之識別碼,如果沒有指定,就傳回呼叫此函式的玩家所加入的公會。 -
orderby
呼叫後,傳回公會成員列表時的排序方式,此參數用字串表示,可以指定用暫存器值、加入公會時間來排序,由於公會暫存器總共有 4 個(其所代表的意義是由開發者依照其遊戲的需要來定義,詳情請參考JoinGuild()
的說明)。
此參數字串中,可用的 Keyword 有以下數種,另外可加後綴 asc 或 desc 表示升序或降序,沒有指定的話,則預設為 asc :
Key | 說明 |
---|---|
r1 | 依 R1 暫存器值排序 |
r2 | 依 R2 暫存器值排序 |
r3 | 依 R3 暫存器值排序 |
r4 | 依 R4 暫存器值排序 |
jointime | 依加入公會的時間 |
例如: ”r1 desc
”
如果一次要指定多個排序條件,則請用「,」分隔, 例如: ”r1 asc,r3 desc, jointime desc
”
-
RegiterCondition
指定暫存器條件,傳回符合條件的公會列表,條件式是由「>」、「<」、 「=」、「<=」、「>=」、「and」、「or」等組成, 例如: ”2<r1<5 and 10>r2 or r3=5 and 1<r4 <50
”
(在不同 overloading,此參數不一定提供,如不提供,表示不過濾暫存器條件) -
Start
列出加入公會時間晚於此時間的成員(開始時間)
(在不同 overloading,此參數不一定提供,如不提供或傳 null 值,則無開始時間) -
End
列出加入公會時間早於此時間的成員(結束時間)
(在不同 overloading,此參數不一定提供,如不提供或傳 null 值,則無結束時間)
公會管理員權限設定與解除
理員就是除了會長以外, 可以幫忙審核招收會員事宜, 會長的遊戲程式可以透過SetGuildManager()
設定某位已經加入公會的會員成為管理員, SuperUser 則是使用suSetGuildManager()
,之後,這位管理員就有權利審核並允許或拒絕其他玩家加入公會。
已經成管理員的玩家, 會員或 Super User 也可以透過同一個函式 SetGuildManager()
、suSetGuildManager()
解除某位管理員的權限。
會員退出公會
會員主動退出公會,會長、管理員要將某玩家踢出公會,可以呼叫 LeaveGuild()
,Super User 則是呼叫 suLeaveGuild()
。
設定會員暫存器值
當會員加入公會之後,就自動擁有一組暫存器 R1, R2, R3, R4 這四個可儲存 int 整數的暫存器,有兩種方法可以設定其值:
- 直接呼叫設定暫存器值的 api 函式
SetPlayerGuildRegister()
,函式定義如下:
csharp
static void SetPlayerGuildRegister(CloudGame game,
object R1, object R2, object R3, object R4,
OnCallCompletion cb, object token)
參數 R1~R4 要設定的暫存器,必須是整數,若不設定,則傳 null
請注意,若玩家可以自行設定自己的公會暫存器,則必須在管理後台將權限打開,否則只能透過 Super User,在 DP 中以 suSetPlayerGuildRegister()
設定。
- 呼叫
SetItemInstanceAttribute()
時同步設定
使用有同步寫入 R1, R2, R3, R4 暫存器的SetItemInstanceAttribute()
overloading 函式, 這些 overloading 多了指定暫存器名稱的字串參數,代表要同步寫入的暫存器。
如果是只寫入一個屬性,就像這樣呼叫:
csharp
CloudItem.SetItemInstanceAttribute(game, iguid, id, attrName, attrValue, "r2", cb, null);
代表同步寫入到呼叫者的 R2 暫存器。
如果是 Super user,請指定 userid 後呼叫 suSetItemInstanceAttribute()
。
另外,若是一次要寫入多個屬性值,使用字串陣列指定屬性名、屬性值時,要同步寫入的暫存器也用字串陣列表示,例如以下的程式寫法:
csharp
String[] attrName={"name","grade","class","enegy","point"};
String[] attrValue={"Bruce","3230","A300","98723","1381"};
String[] SyncR={null,null,null,"r3","r1"};
CloudItem.SetItemInstanceAttribute(game, iguid, id, attrName, attrValue, SyncR, cb, null);
從參數 SyncR 的字串陣列值來看,”r3”與”r1”分別對應到”enegy
”,與”point
”,所以當”enegy
” 寫入屬性值”98723
”時,也將同步將整數 98723
寫到 R3 暫存器;同理, “point
”寫入屬性值”1381
”時,也也將同步將整數 1381
寫到 R1 暫存器。
6-5 信箱信息(Mailbox)
信箱信息又稱為「離線訊息」,有別於 CloudScene 與 CloudGame 的即時訊息,它是讓遊戲程式用來發送非即時通知的訊息給其它玩家,所發送的訊息會先儲存在雲端,並佔用遊戲的儲存額度,但系統不會主動通知收訊人,開發者必須另外透過私人訊息或場景訊息實作即時通知的程式碼。
由於訊息是存放在 SGC 雲端儲存設備裡,如果開發者想要節省空間,可以把在遊戲程式中將訊息下載後存放在玩家本機儲存體裡,然後把已讀的信息刪除,之後玩家遊戲程式起動後,若要列出信息,必須先讀取本機端的已下載信息,再透過 api 讀取雲端訊息,讀取後再存放在本機,然後刪除。依此類推,直到此玩家的訊息庫空了為止。
離線訊息是由一個專用的 api 類別 CloudMailbox 來處理,裡面的函式都是靜態 static method,收發訊息程序非常簡單,共有以下函式:
函式名稱 | 用途說明 |
---|---|
Sendmail() | 寄送信息,寄送時可以選擇要寄給:1. 一般的玩家,以參數指定收信人的 userid 。2. 公會的成員,以參數指定公會的 guildid ,另外再指定一個定義為 enum_Whom_ 的參數,用來指定收信人誰。Whom定義如下:enum _Whom_ { all = 1, manager = 2, leader = 3 }; 。另外,寄送時也可以指定自定的 tag 值,讓收信人讀取時可以依照 tag 過濾。 |
Listmail() | 列出信箱中的信息,也就是讀取信箱信息。 |
DeleteMail() | 刪除信息。 |
SetMailTag() | 設定某信息的 tag 值。 |
解釋:
all: 公會所有的成員 、manager: 公會的管理員(包含會長) 、leader: 公會的會長
寄送信息
- 寄送給一般的玩家:
csharp
void SendMail(CloudGame game,string touserid, int tag, string text, bool hasSentBackup,
OnCallCompletionWithData cb, object token)
- touserid:收信人的 user id
- tag:信件標籤,將來讀取郵件列表時可用來做為過濾條件
- text:信件內容,只能是純文字,長度最大 1K 個字元
-
hasSentBackup true:寄送信息後,寄信人也會有一份寄信備份,寄信備份的 mailid 將在 callbackcb 的參數 data 傳回來。
-
寄送給公會會員:
csharp
void SendMail(CloudGame game, int guildid, _Whom_ whom, int tag, string text, bool hasSentBackup,
OnCallCompletionWithData cb, object token)
guildid
:公會的識別碼,將信息寄給此公會成員-
whom
:指定要寄給公會的那些人,定義如下:enum Whom {all=1, manager=2, leader=3},解釋:all: 寄給公會所有的成員 、manager: 寄給公會的管理員 、leader: 寄給公會的會長。 -
tag
:信件標籤,將來讀取郵件列表時可用來做為過濾條件。 text
:信件內容,只能是純文字,長度最大 1K 個字元。hasSentBackup
:true:寄送信息後,寄信人也會有一份寄信備份,寄信備份的 mailid 將在 callback cb 的參數 data 傳回來。false: 不存放寄信備份。
讀取信息
讀取信箱信息有六個 Overloading。
void ListMail(CloudGame game, int mailid, OnCallCompletionWithData cb, object token)
void ListMail(CloudGame game, Folder folder, OnCallCompletionWithData cb, object token)
void ListMail(CloudGame game, Folder folder, int tag, OnCallCompletionWithData cb, object token)
void ListMail(CloudGame game, Folder folder, DateTime start, DateTime end, OnCallCompletionWithData cb, object token)
void ListMail(CloudGame game, Folder folder, int tag, DateTime start, DateTime end, OnCallCompletionWithData cb, object token)
void ListMail(CloudGame game, Folder folder, int tag, DateTime datetime, Direction direction, OnCallCompletionWithData cb, object token)
依照以下參數別而有不同的讀取方式:
mailid
:指定 mailid,直接讀取該信息。-
folder
:指定 folder(信匣),共有三個 enum 值,定義為 : enum Folder {sent=1, inbox=2, both=3 },解釋: sent: 寄信備份匣 、inbox: 收信匣、both: 兩者。 -
tag
:指定 tag(標籤),由開發者自行定義。 start
,end
:指定開始與結束時間,讀取這兩個時間之內寄送的信息。datetime
,direction
:指定時間與方向,讀取此時間之前或之後寄送的信息direction
兩個值,共有兩個enum
值,定義如下 : enum Direction {before = 1, after = 2 },解釋:before
: 刪除 datetime 之前的信息 、after
: 刪除 datetime 之後的信息。
讀取的郵件透過 callback 的參數 cb 傳回,在 code=0
表示成功時,讀到的郵件列表資料放在參數 data
中,型別為 List Hashtable
,Key/Value 如下表:
Key | Value | Type |
---|---|---|
mailid | 信息識別碼 Mail ID | int |
text | 信息本文 | String |
folder | 信息所在的信匣,1: 寄信備份,2:收信匣 | int |
touserid | folder =2 (收信匣):收信人的 userid、folder=1 (寄信備份):1.字串的第一個字元是「#」,表示收信人是公會成員,「#」之後的字串則為公會的識別碼。2. 字串的第一個字元不是「#」,則為收信人的 userid | String |
tonickname | folder =2 (收信匣):收信人的暱稱、folder=1 (寄信備份):1.寄給公會成員,則為公會名稱。2.寄給玩家個人,則為收信人的 userid | String |
fromuserid | 寄信人的 userid | String |
fromnickname | 寄信人的暱稱 | String |
senddate | 寄信時間 | String |
tag | int |