2010-12-20 3 views
12

Ich baue ein Browser-Spiel mit einer Mini-Karte der Umgebung des Spielers. Ich muss verfolgen, wo andere Spieler sind und diese Minikarte aktualisieren, wenn jemand sich bewegt. Ich implementiere das in NodeJS und CouchDB. Mein Entwurf geht wie folgt:Wie emulieren ich "Schlaf" in NodeJS?

Ich habe eine Datenbank für alle sich ändernden Spieldaten. In dieser Datenbank habe ich ein Dokument, das die grundlegenden Kartendaten in einem zweidimensionalen Array enthält, wobei jedes Quadrat auf dem Gitter durch ein Element in diesem Array dargestellt wird. Da ich viele verschiedene Benutzer haben konnte, die alle gleichzeitig auf der Karte waren, brauchte ich einen Weg, um sicherzustellen, dass ich nicht gelesen werde, während jemand anderes schreibt. Ich könnte fehlerhafte Informationen bekommen, wenn eine Menge Benutzer lesen und Schreiben in ein einziges Dokument). Ich entschloss mich, separate Dokumente in dieser Datenbank zu haben, die die einzelnen Quadrate repräsentieren, und jedes Dokument hat die Spieler, die auf diesem Quadrat "on" sind, und einige andere Daten, die mit diesem Quadrat verbunden sind. Im Wesentlichen wird das Kartendokument nur als Nachschlagetabelle für das quadratische Dokument verwendet. Dadurch kann ich ein einzelnes Dokument ändern, ohne das gesamte Kartendokument neu schreiben zu müssen, wodurch das Problem der gleichzeitigen Lese- und Schreibvorgänge gelöst wird.

Mein Problem ist, dass ich eine Mini-Map für den Benutzer als Referenz verwenden muss. Diese Minikarte enthält die Dokumente der umliegenden Plätze. Da ich das alles zur gleichen Zeit brauche, dachte ich, ich würde einfach alle 9 Quadrate aus der Datenbank holen und sie in einer einzigen Ajax-Antwort zurückgeben. Mein Problem ist jedoch mit der Verringerung der Menge an blockierenden IO, die ich mache. Im Moment habe ich eine verschachtelte Schleife, die die benötigten Quadrate von der Datenbank anfordert. Hier ist ein Blick auf meinen Code (Position und Karte sind vergangen in):

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

Mein Problem ist jedoch, dass GetDoc ist nicht blockierend, so dass ich weiß nicht, wann es die quadratischen Daten zu Minimap gesetzt wird . Ich dachte an so etwas wie dies zu tun:

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

Dies lässt mich warten, bis CouchDB alle meine Daten fertig ist immer, aber JavaScript nicht über eine Sleep-Funktion. Der nächste, den ich gefunden habe, war setTimeout, aber das ist auch nicht blockierend und ich werde nie 100% ig sicher sein, dass die Zeit, die ich für die Zeitüberschreitung gewählt habe, ausreichen würde, dass CouchDB meine Daten abrufen kann.

Also im Grunde möchte ich eine Bedingung haben, testen, dann wieder an den Event-Stack zurückgeben, wenn die Bedingung falsch ist. Die einzige Lösung, die ich dachte, war eine rekursive SetTimeout Rückruf zu haben, die so etwas tun würde:

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

Diese ziemlich schrecklich scheint ... Gibt es eine bessere Art und Weise, dass ich dies tun könnte? Sollte ich diesen Ansatz aufgeben und dort mehrere Ajax-Aufrufe erzwingen, um aktualisierte Daten zu erhalten? Soll ich einen blockierenden Ansatz wählen?

Antwort

5

Just another Rückruf verwenden:

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

Oh, und Ihre Datenbankmodell ist wirklich schlecht, ich weiß nicht viel über das Spiel, aber es sei denn, dies auf mehrere Knoten Prozesse ausgeführt werden muss, ist es Es wäre besser, die Map usw. in einem JS-Array zu belassen und nur in die Datenbank zu schreiben, wenn der Server den Status speichern muss.

Oh, und Sie können auch Ihre keyMap mit einem anonymen Funktionsaufruf ersetzen:

(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

Ich nehme an, ich mache den Map Teil alles falsch. Alles in den Speicher anstatt in die Datenbank zu behalten, ist definitiv ein einfacher Weg, Dinge zu tun. Ich denke, ich werde fortfahren und alles in mein Kartendokument legen und den Zustand nur speichern, wenn ich neu starten muss oder so. Danke für die Hilfe!! – tjameson

+1

Überprüfen Sie auch [Redis] (http://redis.io/). Sie starten den Redis-Server und verwenden dann das Redis-Modul im Knoten. Sie geben einen String-Schlüssel und dann ein Json-Objekt und BAM alle Kinder und Daten gespeichert. Lade es hoch und benutze den Schlüssel, um es als Objekt wieder in den Speicher zu bekommen. – BigOmega

+0

Sie könnten auch [Firebase] (http://firebase.com) als Alternative zu Redis oder einem relationalen Datenspeicher für diese Art von Statusinformationen in Erwägung ziehen. Firebase synchronisiert den Status in Echtzeit mit allen angeschlossenen Clients. Es wurde für Ihre Art der Anwendung entwickelt. – DrFriedParts

1

Da reden wir über Event-ed-System, ich glaube, es sauberer sein würde, wenn Sie nur eine Last getan Ereignis emittieren kann wenn numDone == 9 und fange es von deinem Main und fahre von dort fort.

Sie können die gesamte Karte in redis versetzen, auch wenn das Spiel über mehrere Knoten laufen muss.