2010-12-20 58 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中。