在用于实现基于回合的多人游戏服务器的Meteor应用程序中,客户端通过发布/订阅接收游戏状态,并且可以调用Meteor方法sendTurn
将回合数据发送到服务器(他们无法直接更新游戏状态集合)。
var endRound = function(gameRound) {
// check if gameRound has already ended /
// if round results have already been determined
// --> yes:
do nothing
// --> no:
// determine round results
// update collection
// create next gameRound
};
Meteor.methods({
sendTurn: function(turnParams) {
// find gameRound data
// validate turnParams against gameRound
// store turn (update "gameRound" collection object)
// have all clients sent in turns for this round?
// yes --> call "endRound"
// no --> wait for other clients to send turns
}
});
要实现时间限制,我想等待特定时间段(让客户有时间调用
sendTurn
),然后确定舍入结果-但前提是尚未在sendTurn
中确定舍入结果。我应如何在服务器上实现此时间限制?
我实现这一目标的幼稚方法是调用
Meteor.setTimeout(endRound, <roundTimeLimit>)
。问题:
sendTurn
和endRound
(?)中同步更新集合(没有回调),但这足以消除竞争条件吗? (阅读关于对同步数据库操作的this SO question的可接受答案的第四条评论也会产生效果,我对此表示怀疑)endRound
中调用的setTimeout
函数)是什么意思?最佳答案
很好的问题,而且比看起来要棘手。首先,我想指出的是,我在以下 repo 中实现了针对此确切问题的解决方案:
概括而言,该问题基本上具有以下属性:
sendTurn
)endRound
endRound
(无论如何)endRound
必须每轮准确执行一次,而不管客户端做什么现在,考虑一下我们必须处理的Meteor的属性:
this.unblock()
)。以下方法等待第一个。 这意味着,每当方法调用执行一次屈服操作时,Node或数据库中的值都可以更改。这可能会导致以下潜在的比赛情况(这些只是我已经解决的情况,但可能还有其他情况):
sendTurn
。两者都调用屈服操作来存储转弯数据。然后,这两种方法都会检查是否有2位玩家轮到他们发送回合,找到肯定的答案,然后endRound
运行两次。 sendTurn
。在这种情况下,超时和播放器的方法都会调用endRound
,从而再次运行两次。 endRound
从未被调用。 您可以通过几种方式解决此问题,或者在Node或数据库中进行同步。
endRound
代码移到方法调用本身之外,使用其他方式触发它。这是我采用的方法,可确保只有计时器或最终玩家触发回合的结束,而不是两者都触发(有关observeChanges
的实现,请参见here)。 var currentVal;
while(true) {
currentVal = Foo.findOne(id).val; // yields
if( Foo.update({_id: id, val: currentVal}, {$inc: {val: 1}}) > 0 ) {
// Operation went as expected
// (your code here, e.g. endRound)
break;
}
else {
// Race condition detected, try again
}
}
上面的方法很原始,可能会导致高负载下数据库性能下降。它也不能处理定时器,但是我可以肯定地认为您可以弄清楚如何扩展它以使其更好地工作。
您可能还希望看到此timers code,以了解其他一些想法。一旦有时间,我将把它扩展到您描述的完整设置。
关于node.js - Meteor.setTimeout和Meteor.methods之间的并发,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/32987510/