設計簡易的 Dispatcherless 自動配對機制 - 透過 Firebase Realtime Database
遊戲配對系統中,直接規劃 配對者(Dispatcher) 來執行配對任務是最直接的解決辦法,但耗費較高也比較繁瑣,以下的設計,透過使用者端發起 Transaction 機制來作動配對邏輯,再另外監聽單場遊戲節點來確認遊戲配對狀態與起始資料,可在不需而外 Dispatcher 的狀況下進行配對,適用在固定玩家數量的遊戲類型
排隊結構
透過 子結點 實作 群組配對,並將玩家屬性寫入排隊資料,配對邏輯可以進行 屬性配對
根目錄
群組
資料
Queue /
Group ID /
[ Room ID: 已開啟的房間號碼 User[ ]: 已經報名的玩家,及其屬性 Create: 房間創立時間 ]
玩家進行自動配對
其中 Queue/GroupA 為特定群組,以群組為節點存放等待資訊,也以此節點為 Transaction 執行的標的。其中 game_id, score, low, high 為玩家屬性,實際使用應為變化數值
FIRDatabaseReference* pair_ref = [[[FIRDatabase database] reference] child:@"Queue/GroupA"];
[pair_ref runTransactionBlock:^FIRTransactionResult* _Nonnull(FIRMutableData* _Nonnull currentData) {
NSMutableArray* datas = currentData.value;
if (!datas || [datas isEqual:[NSNull null]]) {
// Queue 空白,將自己排入 Queue
NSDictionary* data = @{
@"game_id" : "UUID GAME ID", // 新建遊戲房的 ID
@"score" : 100, // 玩家目前積分
@"low" : 80, // 配對玩家的積分下限
@"high" : 120, // 配對玩家的積分上限
};
datas = [[NSMutableArray alloc] initWithObjects:data, nil];
} else {
BOOL pair = NO;
for (NSDictionary* data in datas) {
if ([[data objectForKey:@"score"] intValue] >= 80
&& [[data objectForKey:@"score"] intValue] <= 120
&& 100 >= [[data objectForKey:@"low"] intValue]
&& 100 <= [[data objectForKey:@"high"] intValue]) {
// 配對成功 Pair,將等待訊息移除,目標前往加入 [data objectForKey:@"game_id"]
[datas removeObject:data];
break;
}
}
if (!pair) {
// 配對失敗,將自己排入 Queue
NSDictionary* data = @{
@"game_id" : "UUID GAME ID",
@"score" : 100,
@"low" : 80,
@"high" : 120,
};
[datas addObject:data];
}
}
[currentData setValue:datas];
return [FIRTransactionResult successWithValue:currentData];
}
andCompletionBlock:^(NSError* _Nullable error, BOOL committed, FIRDataSnapshot* _Nullable snapshot) {
if (error || !committed) {
// 配對失敗
} else {
// 成功配對,進入下個階段,前往 game
}
}
withLocalEvents:NO];
遊戲結構
透過 Player 來監聽整體的 配對狀態,滿員時即配對完成。Init 則是設計為開場預設資料,統一委由開局者或是關局者來寫入即可
根目錄
-
資料
Game ID / Player /
Player Id /
User Image ... // 遊戲中使用到的玩家基本資料
Game ID / Init /
RandomSeed ... // 遊戲中共同設定資料
配對完成後,加入遊戲。可以相同方法寫入初始值
Player ID A 與 user_json_dic 為此玩家的專屬 ID 及其玩家資料,使用上應替換為玩家數值
FIRDatabaseReference* game_ref = [[[FIRDatabase database] reference] child:@"UUID GAME ID/Player"];
NSData* user_json_string = [[NSString stringWithUTF8String:user_json] dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary* user_json_dic = [NSJSONSerialization JSONObjectWithData:user_json_string options:0 error:nil];
NSDictionary* player = @{
@"Player ID A" : user_json_dic,
};
[game_ref setValue:player];
監聽遊戲的配對狀態
FIRDatabaseReference* listen_ref = [[[FIRDatabase database] reference] child:@"Game/UUID GAME ID"];
FIRDatabaseHandle handle = [listen_ref observeEventType:FIRDataEventTypeValue
withBlock:^(FIRDataSnapshot* _Nonnull snapshot) {
if (snapshot.value != [NSNull null]) {
// 監聽遊戲房間副本
}
}];
Last updated