我试图了解以下情况的正确方法:
多人游戏,每个游戏的结构only
有两名玩家。每场比赛/比赛将完全随机化
让我们假设5个用户"日志"同时进入我的应用程序,每个人都在"搜索"比赛。每个用户都拥有一个名为opponent
的属性,该属性等于对手uniqueID
(初始值等于""
。到目前为止一直很好。
假设用户1与用户3匹配。用户1会将自己的oppoent
值更新为用户3 uniqueID
,并对用户3执行相同操作
问题
1)如果在同一时刻,用户2试图与用户3相同怎么办? 2)如果在同一时刻,用户3试图对用户4这样做怎么办?
要点
是否可以锁定"用户值?或者一旦改变就冻结它们?我的方法是错误的吗?
我在考虑使用Security Rules
和Validation
来创建一致性,但我可能选择了错误的技术(FireBase)。有什么想法吗?
修改
我尝试过的安全规则,仍然由于某种原因启用了第三个设备更改"已经改变了对手"值。
{
"rules": {
".read": true,
".write": true,
"Users" :
{
"$uid" : {
"opponent" :
{
".write" : "data.val() == 'empty' || data.val() == null",
".validate": "data.val() == null || data.val() == 'empty' || newData.parent().parent().child(newData.val())
.child('opponent').val() == $uid"
}
,".indexOn": "state"
}
}
}
}
答案 0 :(得分:4)
您可以使用Firebase security rules验证很多内容。
例如,你可以说只有当前没有对手的对手才能写出对手:
"users": {
"$uid": {
"opponent: {
".write": "!data.exists()"
}
}
}
通过以下操作:
ref.child('users').child(auth.uid).child('opponent').set('uid:1234');
ref.child('users').child(auth.uid).child('opponent').set('uid:2345');
第二个set()
操作将失败,因为opponent
属性此时已有值。
你可以扩展它以验证对手必须互相引用:
"users": {
"$uid": {
"opponent: {
".write": "!data.exists()"
".validate": "newData.parent().parent().child(newData.val())
.child('opponent').val() == $uid"
}
}
}
opponent
开始,我们将两个级别上升回users
:newData.parent().parent()
。 child(newData.val())
。 opponent
属性是否与我们的uid匹配:child('opponent').val() == $uid
。现在来自上面的两个写操作都会失败,因为它们一次只设置一个对手。要解决此问题,您需要执行所谓的multi-location update:
var updates = {};
updates['users/'+auth.uid+'/opponent'] = 'uid:1234';
updates['users/uid:1234/opponent'] = auth.uid;
ref.update(updates);
我们现在向Firebase服务器发送一个update()
命令,将uid写入两个对手。这将满足安全规则。
一些注意事项:
答案 1 :(得分:2)
您还可以查看transaction operation。
Firebase事务确保您正在执行的当前数据集实际上是数据库中的数据,从而保证您正在更新处于正确状态的数据。文档表明,这是避免竞争条件的推荐方法,如您所描述的。
像这样(在IOS中,警告 - 未经测试):
NSString* user1Key = @"-JRHTHaIs-jNPLXOQivY";
NSString* user2Key = @"-NFHUaIs-kNPLJDHuvY";
Firebase *user1Ref = [[Firebase alloc] initWithUrl: @"https://docs-examples.firebaseio.com.users/-JRHTHaIs-jNPLXOQivY/opponent"];
Firebase *user2Ref = [[Firebase alloc] initWithUrl: @"https://docs-examples.firebaseio.com.users/-NFHUaIs-kNPLJDHuvY/opponent"];
//See if the proposed opponent does not yet have a match
[user2Ref runTransactionBlock:^FTransactionResult *(FMutableData *opponent) {
if (opponent.value == [NSNull null]) {
//They have no match - update with our key and signal success
[opponent setValue:user1Key];
return [FTransactionResult successWithValue: opponent];
} else {
return [FTransactionResult abort]; //They already have an opponent - fail
//Notify the user that the match didn't happen
}
} andCompletionBlock:^(NSError *error, BOOL committed, FDataSnapshot *snapshot) {
if (!error && committed) {
//The transaction above was committed with no error
//Update our record with the other player - we're matched!
[user1ref setValue:user2Key];
//Do whatever notification you want
} else {
//Notify that the matchup failed
}
}];