Как мне эмулировать "сон" в NodeJS?
Я строю браузерную игру с мини-картой окрестностей игрока. Мне нужно отслеживать, где находятся другие игроки, и обновлять эту мини-карту всякий раз, когда кто-то движется. Я реализую это в NodeJS и CouchDB. Мой дизайн выглядит следующим образом:
У меня есть база данных для всех изменяющихся игровых данных. В этой базе данных у меня есть документ, который содержит основные данные карты в двумерном массиве, где каждый квадрат на сетке представлен элементом в этом массиве. Поскольку у меня могло быть множество разных пользователей, перемещающихся по карте одновременно, мне нужно было каким-то образом убедиться, что я не получаю чтение, поскольку кто-то другой пишет на него (я мог бы получить неверную информацию, если тонна пользователей читает и запись в один документ). Я решил иметь отдельные документы в этой базе данных, которые представляют отдельные квадраты, и у каждого документа есть игроки, которые находятся на этом квадрате, и некоторые другие данные, связанные с этим квадратом. По сути, документ карты используется только как справочная таблица для квадратного документа. Это позволяет мне изменять один документ без необходимости переписывать весь документ карты, решая проблему одновременного чтения и записи.
Моя проблема, однако, заключается в том, что мне нужно получить мини-карту, чтобы пользователь мог использовать ее в качестве справочного материала. Эта мини-карта будет иметь документы на окружающие площади. Поскольку мне нужно все это одновременно, я решил просто взять все 9 квадратов из базы данных и вернуть их в одном ответе на ajax. Моя проблема, однако, заключается в уменьшении количества блокирующих операций ввода-вывода, которые я делаю. Прямо сейчас у меня есть вложенный цикл, который запрашивает нужные мне квадраты из базы данных. Вот посмотрите на мой код (позиция и карта переданы в):
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 не блокирует, поэтому я не знаю, когда он установит квадратные данные в miniMap. Я думал о том, чтобы сделать что-то вроде этого:
while(numDone < 9){
sleep(10);
}
callback(miniMap);
Это позволит мне подождать, пока CouchDB не завершит получение всех моих данных, но JavaScript не имеет функции сна. Самым близким, что я нашел, был setTimeout, но он также неблокирующий, и я никогда не буду на 100% уверен, что выбранное мной время ожидания будет достаточно, чтобы CouchDB мог завершить получение моих данных.
Поэтому я хочу иметь условие, проверить его, а затем вернуть его в стек событий, если условие ложно. Единственное решение, о котором я подумал, было иметь рекурсивный обратный вызов setTimeout, который бы делал что-то вроде этого:
function testCallback(data, condition, callback){
if(data < condition){
setTimeout(function(){
testCallback(data, condition, callback);
}, 10);
}else{
callback(data);
}
}
Это кажется довольно ужасным... Есть ли лучший способ, которым я мог бы сделать это? Должен ли я отказаться от этого подхода и заставить несколько вызовов ajax получать обновленные данные? Должен ли я пойти на блокирующий подход?
2 ответа
Просто используйте другой обратный вызов:
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
}
});
}
}
}
Да, и ваша модель базы данных действительно плохая, я не очень разбираюсь в вашей игре, но если для этого не нужно запускать несколько процессов Node, было бы лучше сохранить карту и т. Д. В массиве JS и писать только в БД, когда серверу нужно сохранить состояние.
Ох, и вы также можете заменить свой 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
Учитывая, что мы говорим о системе с событиями, я думаю, что было бы чище, если бы вы просто выдавали событие load done, когда numDone==9, и перехватывали его из своего главного и продолжали оттуда.
Вы можете перерисовать всю карту, даже если игра должна быть запущена на нескольких узлах.