簡體   English   中英

Firebase Function 等待兩個標志設置為 true 如果兩個標志同時設置為 true,則運行兩次

[英]Firebase Function waits for two Flags to be set to true is ran twice if both are set to true at the same time

我正在使用 Firebase 作為后端制作基於回合的口袋妖怪風格游戲 rpg 戰斗游戲。 In a 1v1 turn based battle, players select the moves they want to use, and I have them call an HTTP function which writes their chosen moves to the Realtime DB, and also update a flag to true, either P1SkillsSelected, or P2SkillsSelected depending on if它們是 P1 或 P2。

Select 技能 function:

const pvpSelectSkills = (data, context) => {

const battleRoomKey = data.battleRoomKey;
const uid = context.auth.uid;  // Firebase UID
const selectedSkills = data.selectedSkills;  // A JSON string from the client
var playerName;
var returnResults = { resultCode: 0 };

var promises = [];

promises.push(db.ref('/pvpCombatRooms/' + battleRoomKey).once("value"));

return Promise.all(promises)
.then(results => {

    console.log("player " + uid + " selected their skills for pvp");

    var updates = {};
    var player1Id = results[0].val()['player1Id'];
    var player2Id = results[0].val()['player2Id'];

    // ============================= VERIFICATION STEPS =============================

    // ========== Check that combatroom has actionExpected set to confirmMonsters ==========
    if (results[0].val()['actionExpected'] !== constants.ACTION_EXPECTED_SELECT_SKILLS){
        console.log("Room was not expecting a select SKILLS action. Exiting");
        throw new helpers.BreakSignal();
    }

    // ========== Check that the playerID matches either player1Id or player2Id variable in this combat room ==========
    if (uid === player1Id) {
        console.log("player is player1");
        playerName = "p1";
    } else if (uid === player2Id) {
        console.log("player is player2");
        playerName = "p2";
    } else {
        console.log("UID of function caller did not match uid of player1 or player2. Exiting");
        throw new helpers.BreakSignal();
    }

    updates['/pvpCombatRooms/' + battleRoomKey + '/skillsSelected/' + playerName + "SkillsSelected"] = true   // update skills selected to true
    updates['/pvpCombatRooms/' + battleRoomKey + '/' + playerName + "Skills"] = selectedSkills;  // Write the skills themselves

    // ============================= VERIFICATION SUCCESS =============================

    db.ref().update(updates, function(error) {
        if (error) {
            console.log("Error updating data:", error);
        }
    });

    returnResults = { resultCode: 1 };
    return returnResults;

}).catch(error => {
    console.log('Error in wild monster combat monster select: ' + error);
    return null;
});
};module.exports = pvpSelectSkills;

我制作了一個監聽器 function 來監聽這些標志的 onWrite:

functions.database.ref('/pvpCombatRooms/{battleRoomKey}/skillsSelected/{values}').onWrite(pvpResolveSkills)

當只有一個設置為真時,function 退出,因為兩個玩家都需要 select 他們的技能才能解決這一輪。 如果兩者都設置為 true,則執行游戲邏輯。

const pvpResolveSkills = (data, context) => {

battleRoomKey = context.params.battleRoomKey; 
var firebaseUpdates = {};

var results;
var promises = [];  
      
promises.push(db.ref('pvpCombatRooms/' + battleRoomKey).once("value"));
promises.push(db.ref(`pvpCombatRooms/${battleRoomKey}/skillsSelected`).once('value'));

return Promise.all(promises)
.then(res => {

    battleRoomResult = res[0].val();
    var resolvedMoves = [];  // An array of resolvedMove objects to be sent back to the player

    let confirmedSnapshot = res[1].val();
    for (const key of Object.keys(confirmedSnapshot)) {
        if (confirmedSnapshot[key] === false) { 
          console.log("one or more players not ready. Exiting");
          return null;
         }
      }.......................

我的問題是,如果兩個玩家幾乎同時確認他們想要使用的技能,那么 onWrite 監聽器,因為它必須對引入足夠延遲的 DB 進行讀取調用,將完全執行兩次,這意味着游戲邏輯 function 將運行兩次,角色將受到雙重傷害等。

有沒有辦法在保證偵聽器不會多次將兩個標志都讀取為真的同時構建它? 我認為沒有辦法讓它“冪等”(即使它執行兩次,它也會對數據庫產生相同的寫入)。

The only solution I can think of at this time is if the listener function had immediate access to the state of both p1SkillsSelected and p2SkillSelected, but Firebase can't listen to changes on an entire node, therefore I'm forced to listen to changes on兩個子節點(p1SkillsSelected 和 p2SkillSelected)分別,如果我想訪問另一個 var,我需要查詢數據庫,這會導致兩個 var 在偵聽器的兩個調用上都設置為 true 的足夠延遲。

可能這里最好的方法是使用transactions

來自 Firebase 實時數據庫文檔:

在處理可能被並發修改損壞的數據時,例如增量計數器,您可以使用事務操作。 你可以給這個操作一個更新 function 和一個可選的完成回調。 更新 function 將數據的當前 state 作為參數,並返回您想要寫入的新的所需 state。 如果另一個客戶端在您的新值成功寫入之前寫入該位置,則使用新的當前值再次調用您的更新 function,並重試寫入。

...

如果事務被拒絕,服務器將當前值返回給客戶端,客戶端使用更新后的值再次運行事務。 這會一直重復,直到交易被接受或您中止交易。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM