我有一个Android应用程序(游戏),匹配2个人,让他们玩。我正在使用firebase实时数据库,并在Node.js上为此编写了必要的代码。在我的数据库中,当有人点击“在线按钮”时,它触发firebase功能并使玩家状态等待(状态:等待)并将他/她与另一个状态也在“等待”的玩家匹配,如果操作是成功的node.js更新“ rakip“节点与对手的用户名为两个玩家的进一步行动。
然而,我担心的是当2个用户同时搜索对手时(比如说用户A和B)可能会有不好的情况,例如用户A与B匹配而用户B与C匹配。我怎样才能防止这会发生吗?如果他/她的状态发生变化,我怎么能停止B的搜索(node.js动作)?
提前致谢。
我的实时firebase数据库是这样的:
My-project-name
users
sinan
atilansayi: ""
gal: false
lose: 0
rakip: "hakan"
rakipsallama: ""
sayilar: "4567"
sonuc: ""
status: "on"
turn: false
win: 1
hakan
atilansayi: 5678
gal: false
lose: 1
rakip: "sinan"
rakipsallama: "7559|13238436"
sayilar: 7559
sonuc: "-"
status: "on"
turn: true
win: 0
mehmet
atilansayi: 9000
gal: "no"
lose: 0
rakip: "hakan"
rakipsallama: 3333
sayilar: 7000
sonuc: "+"
status: "on"
turn: false
win: 0
我的Node.js文件是:
const functions = require('firebase-functions');
const admin = require('firebase-admin');
admin.initializeApp(functions.config().firebase);
exports.sayi = functions.database.ref("/users/{uid}/status").onWrite(event => {
var status = event.data.val();
var user = event.data.ref.parent.key;
if (status ==="waiting") {
const events = admin.database().ref('users');
const query =events.orderByChild('status').equalTo('waiting').limitToFirst(2);
query.on("value",
function(data) {
var waitinggameusers= data.val();
var keys = Object.keys(waitinggameusers);
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
if(key!==user){
//////////////这是问题可能发生的部分/////////////////
admin.database().ref("/users/"+user+"/rakip").set(key);
admin.database().ref("/users/"+key+"/rakip").set(user);
admin.database().ref("/users/"+user+"/status").set("on");
admin.database().ref("/users/"+key+"/status").set("on");
return console.log("bbbb"+" "+key);
//////////////这是问题可能发生的部分/////////////////
}
}
}, sorunlu);
}
});
exports.sayiat = functions.database.ref("/users/{uid}/atilansayi").onWrite(event => {
var sallanansayi ="";
var rakibinsayisi ="";
var res = event.data.val();
if(res===""){
return true;
}
var res2 = res.split("|");
sallanansayi += res2[0];
sallanansayi2=sallanansayi;
var user = event.data.ref.parent.key;
var sonuc ="";
var artilar ="";
return admin.database().ref('users/'+user+'/rakip').once('value').then(function(snapshot){
var rakip = snapshot.val();
console.log("Rakip: "+rakip);
return admin.database().ref('users/'+rakip+'/sayilar').once('value').then(function(sayisinibul){
rakibinsayisi += sayisinibul.val();
for (i = 0; i < sallanansayi2.length; i++) {
if(sallanansayi2.charAt(i)===rakibinsayisi.charAt(i)){
sonuc +="+";
artilar += i;
}
}
console.log("Sonuç(sadece artılar): "+sonuc);
console.log("Artıların yerleri: "+artilar);
console.log("Artiların sayısı: "+artilar.length);
for (i = 0; i < artilar.length; i++) {
sallanansayi2 = sallanansayi2.slice(0, artilar.charAt(i)) +"a"+ sallanansayi2.slice(Number(artilar.charAt(i))+1);
rakibinsayisi = rakibinsayisi.slice(0, artilar.charAt(i)) +"b"+ rakibinsayisi.slice(Number(artilar.charAt(i))+1);
}
console.log("Atılan sayı: "+sallanansayi2);
console.log("Rakibin sayısı: "+rakibinsayisi);
for (i = 0; i < sallanansayi2.length; i++) {
if(rakibinsayisi.indexOf(sallanansayi2.charAt(i))!==-1){
sonuc += "-";
var x= rakibinsayisi.indexOf(sallanansayi2.charAt(i));
rakibinsayisi= rakibinsayisi.slice(0,x)+"y"+rakibinsayisi.slice(x+1);
sallanansayi2= sallanansayi2.slice(0,i)+"x"+sallanansayi2.slice(i+1);
}
}
var randomnumber = getRndInteger(1,100000000);
if(sonuc===""){
sonuc ="x";
}else if(sonuc==="++++"){
admin.database().ref('users/'+user+'/win').transaction(count => {
if (count === null) {
return count = 1
} else {
return count + 1
}
})
admin.database().ref('users/'+rakip+'/lose').transaction(count => {
if (count === null) {
return count = 1
} else {
return count + 1
}
})
admin.database().ref("/users/"+user+"/turn").set(false);
admin.database().ref("/users/"+rakip+"/turn").set(true);
admin.database().ref("/users/"+user+"/sonuc").set(sonuc+"|"+randomnumber);
return admin.database().ref("/users/"+rakip+"/rakipsallama").set(sallanansayi+"|"+randomnumber);
//return admin.database().ref("/users/"+user+"/gal").set(true);
}
console.log("Rakibin sayısı(son): "+rakibinsayisi);
console.log("Sallanan sayı(son): "+sallanansayi2);
console.log("Sonuç: "+sonuc);
admin.database().ref("/users/"+user+"/turn").set(false);
admin.database().ref("/users/"+rakip+"/rakipsallama").set(sallanansayi+"|"+randomnumber);
admin.database().ref("/users/"+rakip+"/turn").set(true);
return admin.database().ref("/users/"+user+"/sonuc").set(sonuc+"|"+randomnumber);
});
});
});
function sorunlu(error) {
console.log("Something went wrong.");
return console.log(error);
}
function getRndInteger(min, max) {
return Math.floor(Math.random() * (max - min) ) + min;
}
/////
Frank的数据库规则建议与读/写访问相结合
{
"rules": {
"users": {
"$uid": {
".read": "auth.uid == $uid",
".write": "auth.uid == $uid",
"status": {
".validate": "newData.val() === 'on' || data.val() === 'off'|| data.val() === 'waiting'"
},
"rakip": {
".validate":
"newData.parent().parent().child(newData.val()).child('rakip').val() === $uid"
}
}
}
}
}
答案 0 :(得分:2)
这里强制执行一致性的最佳方法似乎是多位置更新。通过多位置更新,您可以在一次调用中向服务器发送多个更新,并且服务器强制执行所有写入操作,或者不会发生任何更新。
您的代码段:
admin.database().ref("/users/"+user+"/rakip").set(key);
admin.database().ref("/users/"+key+"/rakip").set(user);
admin.database().ref("/users/"+user+"/status").set("on");
admin.database().ref("/users/"+key+"/status").set("on");
可以使用以下内容重写为单个多位置更新:
var updates = {};
updates["/users/"+user+"/rakip"] = key;
updates["/users/"+key+"/rakip"] = user;
updates["/users/"+user+"/status"] = "on";
updates["/users/"+key+"/status"] = "on";
admin.database().ref().update(updates);
现在,您可以在服务器上使用安全规则来确保原始数据未被修改。
如果您只能将状态设置为"on"
当前"off"
,则您需要/users/$user/status
验证:
".validate": "newData.val() === 'on' && data.val() === 'off'"
更棘手的是:确保只有在设置了该用户的值时才能设置"rakip"
值,验证/users/$user/rakip
:
".validate": "newData.parent().parent().child(newData.val()).child('rakip').val() === $user"
这些是:
{
"rules": {
"users": {
"$user": {
"status": {
".validate": "newData.val() === 'on' && data.val() === 'off'"
},
"rakip": {
".validate": "newData.parent().parent().child(newData.val()).child('rakip').val() === $user"
}
}
}
}
}