2010-12-20 11 views
12

Estoy construyendo un juego de navegador con un mini mapa de los alrededores del jugador. Necesito rastrear dónde están otros jugadores y actualizar este mini mapa cada vez que alguien se mueve. Estoy implementando esto en NodeJS y CouchDB. Mi diseño es el siguiente:¿Cómo emulo "dormir" en NodeJS?

Tengo una base de datos para todos los datos de juego cambiantes. En esta base de datos, tengo un documento que contiene los datos básicos del mapa en una matriz bidimensional, con cada cuadrado en la cuadrícula representado por un elemento en esta matriz. Como podía tener cientos de usuarios diferentes que se movían por el mapa simultáneamente, necesitaba alguna manera de asegurarme de que no obtuve una lectura porque alguien más está escribiendo en ella (podría obtener información defectuosa si un montón de usuarios están leyendo y escribiendo en un solo documento). Decidí tener documentos separados en esta base de datos que representan los cuadrados individuales, y cada documento tiene los jugadores que están "en" ese cuadrado y algunos otros datos asociados con ese cuadrado. Esencialmente, el documento de mapa se usa solo como una tabla de búsqueda para el documento cuadrado. Esto me permite cambiar un solo documento sin tener que reescribir todo el documento del mapa, resolviendo el problema de las lecturas y escrituras simultáneas.

Mi problema, sin embargo, es que necesito obtener un mini mapa para que el usuario lo use como referencia. Este mini mapa tendrá los documentos de las plazas circundantes. Como necesito todo esto al mismo tiempo, pensé que simplemente tomaría los 9 cuadrados de la base de datos y los devolvería en una sola respuesta ajax. Mi problema, sin embargo, es reducir la cantidad de bloqueos IO que hago. En este momento, tengo un bucle anidado que solicita los cuadrados que necesito de la base de datos. He aquí un vistazo a mi código (posición y el mapa se pasan en):

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++; 
     }); 
    } 
} 

Mi problema, sin embargo, es que GetDoc es no bloqueante, por lo que no sabe cuándo va a establecer los datos de cuadrados a MiniMAP . Pensé en hacer algo como esto:

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

Esto me deja esperar hasta que se termine CouchDB conseguir todos mis datos, pero JavaScript no tiene una función de apagado automático. Lo más parecido que encontré fue setTimeout, pero tampoco es un bloqueo y nunca estaré 100% seguro de que el tiempo que elegí para el tiempo de espera será suficiente para permitir que CouchDB termine de obtener mis datos.

Así que, básicamente, quiero tener una condición, probarla, luego devolverla a la pila de eventos nuevamente si la condición es falsa. La única solución que se me ocurrió fue tener una devolución de llamada setTimeout recursivo que hacer algo como esto:

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

Esto parece bastante terrible ... ¿Hay una mejor manera de que podía estar haciendo esto? ¿Debo abandonar este enfoque y forzar que haya múltiples llamadas ajax para obtener datos actualizados? ¿Debo ir por un enfoque de bloqueo?

Respuesta

5

Sólo tiene que utilizar otra devolución de llamada:

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 
       } 
      }); 
     } 
    } 
} 

Ah, y su modelo de base de datos es realmente mal, no sé mucho acerca de su juego, pero a menos que esto tiene que ejecutar en los procesos de varios nodos, se sería mejor mantener el mapa, etc. en una matriz JS y solo escribir en la base de datos cuando el servidor necesite guardar el estado.

Ah, y también se puede reemplazar su keyMap con una función llamada anónima:

(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

Supongo que estoy haciendo la parte del mapa todo mal. Mantener todo en la memoria en lugar de la base de datos definitivamente será una forma más fácil de hacer las cosas. Creo que continuaré y pondré todo en mi documento de Mapa y solo guardaré el estado cuando necesite reiniciar o algo así. ¡¡Gracias por la ayuda!! – tjameson

+1

También echa un vistazo a [Redis] (http://redis.io/). Inicia redis-server y luego utiliza el módulo redis en el nodo. Usted da una clave de cadena y luego un objeto json, y BAM todos los hijos y datos guardados. Cargalo arriba y use la tecla para regresarlo a la memoria como el objeto nuevamente. – BigOmega

+0

También puede considerar [Firebase] (http://firebase.com) como una alternativa a Redis o un almacén de datos relacional para este tipo de información de estado. Firebase sincronizará el estado en tiempo real con todos los clientes conectados. Fue diseñado para su tipo de aplicación. – DrFriedParts

1

Teniendo en cuenta que estamos hablando de sistema de eventos de opinión, yo creo que sería más limpio si sólo se puede emitir un evento de carga realizada cuando numDone == 9 y atraparlo desde su main y proceder desde allí.

Puede poner el mapa completo en rojo, incluso el juego debe ejecutarse en varios nodos.