2010-12-20 72 views
12

我正在用播放器周圍的迷你地圖製作瀏覽器遊戲。我需要跟蹤其他玩家的位置,並在有人移動時更新此迷你地圖。我正在NodeJS和CouchDB中實現它。我的設計如下:如何在NodeJS中模擬「睡眠」?

我有一個數據庫,用於所有不斷變化的遊戲數據。在這個數據庫中,我有一個包含二維數組中基本地圖數據的文檔,網格中的每個正方形都由此數組中的一個元素表示。由於我可以有大量不同的用戶同時在地圖上移動,所以我需要一些方法來確保我沒有像其他人寫信給它一樣讀取信息(如果大量用戶正在閱讀並且可以收到錯誤信息寫入單個文檔)。我決定在這個數據庫中有單獨的文件,代表各個方塊,每個文件都有「在」該方塊的球員以及與該方塊相關的一些其他數據。實質上,地圖文檔僅用作方形文檔的查找表。這使我可以更改單個文檔而不必重寫整個地圖文檔,解決了同時讀寫問題。

我的問題是,我需要獲取一個迷你地圖供用戶作爲參考。這個迷你地圖將有周圍廣場的文件。由於我同時需要所有這些,我認爲我只需從數據庫中抓取所有9個方格並將其返回到單個ajax響應中。我的問題雖然是減少我做的阻塞IO的數量。現在,我有一個嵌套的循環,請求從數據庫中需要的正方形。下面就來看看我的代碼(位置和地圖都傳遞):

var miniMap = new Array(); 
var keyMap = new Object(); 
var numDone = 0; 
for(i = position.y - 1, y = 0; i < position.y + 1 && i < map.length; i++, y++){ 
    miniMap[i] = new Array(); 
    for(v = position.x - 1, x = 0; v < position.x + 1 && v < map.length; v++, x++){ 
     keyMap[map[i][v].id] = {'x': x, 'y': y}; 
     gameDB.getDoc(map[i][v].id, function(er, doc){ 
      var tPos = keyMap[doc._id]; 
      miniMap[tPos.y][tPos.x] = doc; 
      numDone++; 
     }); 
    } 
} 

我的問題,不過,是的getDoc是非阻塞的,所以我不知道什麼時候會設定方數據微縮。我想過做這樣的事情:

while(numDone < 9){ 
    sleep(10); 
} 
callback(miniMap); 

這將讓我等到CouchDB的完成我的所有數據,但JavaScript並沒有睡眠功能。我發現最接近的是setTimeout,但這也是非阻塞的,我絕對不會100%確定我選擇超時的時間足以讓CouchDB完成獲取我的數據。

所以基本上我想有一個條件,測試它,然後再返回到事件堆棧,如果條件爲false。我認爲唯一的解決辦法是有一個遞歸的setTimeout回調會做這樣的事情:

function testCallback(data, condition, callback){ 
    if(data < condition){ 
     setTimeout(function(){ 
      testCallback(data, condition, callback); 
     }, 10); 
    }else{ 
     callback(data); 
    } 
} 

這似乎是非常可怕的......有沒有更好的辦法,我可以這樣做呢?我是否應該放棄這種方法並強制要求多個Ajax調用來獲取更新的數據?我應該採取阻斷方法嗎?

回答

5

只需使用另一個回調:

function getMiniMap(....., callback) { // supply the callback that sends the data 
    var miniMap = new Array(); 
    var keyMap = new Object(); 
    var numDone = 0; 
    ... 
       numDone++; 
       if (numDone === 9) { // as soon as everything has been collected... 
        callback(miniMap); // ... call the send callback and supply it the miniMap 
       } 
      }); 
     } 
    } 
} 

哦,你的數據庫模型是真的不好,我不知道很多關於你的遊戲,但除非這需要在多個節點的進程中運行,它最好將Map等放在一個JS Array中,並且只在服務器需要保存狀態時寫入數據庫。

哦,你也可以用匿名函數調用替換您的keyMap

(function(x, y) { 
     gameDB.getDoc(map[i][v].id, function(er, doc){ 
      var tPos = keyMap[doc._id]; 
      miniMap[tPos.y][tPos.x] = doc; 
      numDone++; 
     }); 
    })(x, y); // pass in a copy of x and y 
+1

我想我在做Map部分都是錯的。將所有內容保存在內存中而不是數據庫肯定是一種更簡單的方法。我想我會繼續並將所有內容都放入我的Map文檔中,並且只在需要重新啓動時才保存狀態。謝謝您的幫助!! – tjameson 2010-12-20 20:01:09

+1

同時查看[Redis](http://redis.io/)。您啓動redis-server,然後在節點中使用redis模塊。你給一個字符串鍵和一個JSON對象,並且BAM保存所有的孩子和數據。加載它並使用該鍵再次將其作爲對象重新回到內存中。 – BigOmega 2012-12-28 04:18:07

+0

對於此類型的狀態信息,您可能還會考慮將[Firebase](http://firebase.com)作爲Redis或關係數據存儲的替代方案。 Firebase將實時同步所有連接的客戶端。它專爲您的應用類型而設計。 – DrFriedParts 2013-07-09 22:52:37

1

鑑於我們所談論的事件編系統,我認爲這將是清潔的,如果你可以只發出負載完成事件當numDone == 9並從你的主接收並從那裏繼續。

即使需要在多個節點上運行遊戲,也可以將整個地圖放入redis中。