In diesen Tagen versuche ich, die Architektur eines neuen MMORPG-Handyspiels für mein Unternehmen zu entwerfen. Dieses Spiel ähnelt Mafia Wars, iMobsters oder RISK. Grundlegende Idee ist, eine Armee vorzubereiten, um Ihre Gegner (Online-Benutzer) zu bekämpfen.
Ich habe bereits an mehreren mobilen Apps gearbeitet, aber das ist etwas Neues für mich. Nach viel Mühe habe ich eine Architektur entwickelt, die anhand eines allgemeinen Flussdiagramms veranschaulicht wird:
Wir haben uns für das Client-Server-Modell entschieden. Es wird eine zentrale Datenbank auf dem Server geben. Jeder Client verfügt über eine eigene lokale Datenbank, die mit dem Server synchronisiert bleibt. Diese Datenbank fungiert als Cache zum Speichern von Dingen, die sich nicht häufig ändern, z. B. Karten, Produkte, Inventar usw.
Mit diesem Modell bin ich nicht sicher, wie ich die folgenden Probleme angehen soll:
- Wie lassen sich Server- und Client-Datenbanken am besten synchronisieren?
- Soll ein Ereignis in der lokalen Datenbank gespeichert werden, bevor es auf dem Server aktualisiert wird? Was passiert, wenn die App aus irgendeinem Grund beendet wird, bevor Änderungen an der zentralen Datenbank gespeichert werden?
- Dienen einfache HTTP-Anforderungen zur Synchronisierung?
- Woher wissen, welche Benutzer aktuell angemeldet sind? (Eine Möglichkeit besteht darin, dass der Client alle x Minuten eine Anforderung an den Server sendet, um zu benachrichtigen, dass er aktiv ist. Andernfalls wird ein Client als inaktiv eingestuft.)
- Sind clientseitige Validierungen ausreichend? Wenn nicht, wie kann eine Aktion rückgängig gemacht werden, wenn der Server etwas nicht validiert?
Ich bin nicht sicher, ob dies eine effiziente Lösung ist und wie sie skaliert. Ich würde mich sehr freuen, wenn Leute, die bereits an solchen Apps gearbeitet haben, ihre Erfahrungen teilen können, die mir helfen könnten, etwas Besseres zu finden. Danke im Voraus.
Zusätzliche Information:
Die Client-Seite ist in der C ++ - Spiel-Engine Marmelade implementiert. Dies ist eine plattformübergreifende Spiele-Engine, mit der Sie Ihre App auf allen gängigen mobilen Betriebssystemen ausführen können. Threading können wir sicher erreichen und das zeigt auch mein Flussdiagramm. Ich plane, MySQL für Server und SQLite für Client zu verwenden.
Dies ist kein rundenbasiertes Spiel, daher gibt es nicht viel Interaktion mit anderen Spielern. Der Server stellt eine Liste der Online-Spieler zur Verfügung und Sie können gegen sie kämpfen, indem Sie auf die Schaltfläche "Kampf" klicken. Nach einigen Animationen wird das Ergebnis bekannt gegeben.
Für die Datenbanksynchronisation habe ich zwei Lösungen im Sinn:
- Speichern Sie den Zeitstempel für jeden Datensatz. Verfolgen Sie auch, wann die lokale Datenbank zuletzt aktualisiert wurde. Wählen Sie bei der Synchronisierung nur die Zeilen mit einem größeren Zeitstempel aus und senden Sie sie an die lokale Datenbank. Behalten Sie ein isDeleted-Flag für gelöschte Zeilen bei, damit sich jede Löschung einfach wie eine Aktualisierung verhält. Ich habe jedoch ernsthafte Zweifel an der Leistung, da wir bei jeder Synchronisierungsanforderung die gesamte Datenbank scannen und nach aktualisierten Zeilen suchen müssten.
- Eine andere Technik könnte darin bestehen, ein Protokoll über jede Einfügung oder Aktualisierung zu führen, die für einen Benutzer ausgeführt wird. Wenn die Client-App nach einer Synchronisierung fragt, rufen Sie diese Tabelle auf und finden Sie heraus, welche Zeilen welcher Tabelle aktualisiert oder eingefügt wurden. Sobald diese Zeilen erfolgreich an den Client übertragen wurden, entfernen Sie dieses Protokoll. Aber dann überlege ich, was passiert, wenn ein Benutzer ein anderes Gerät verwendet. Gemäß der Protokolltabelle wurden alle Aktualisierungen für diesen Benutzer übertragen, aber tatsächlich wurde dies auf einem anderen Gerät durchgeführt. Daher müssen wir möglicherweise auch das Gerät im Auge behalten. Das Implementieren dieser Technik ist zeitaufwändiger, aber nicht sicher, ob sie die erste ausführt.
quelle
Antworten:
Wenn es sich nicht um ein "Echtzeit" -Spiel handelt, in dem Sinne, dass die Spieler das unmittelbare Ergebnis der Aktionen eines anderen Spielers in einer Spielszene nicht sehen müssen, sollten Sie mit HTTP-Anforderungen einverstanden sein. Beachten Sie jedoch den Overhead von HTTP.
Die Verwendung von HTTP erspart Ihnen jedoch nicht die sorgfältige Gestaltung Ihres Kommunikationsprotokolls. Wenn Sie jedoch sowohl für den Server als auch für den Client verantwortlich sind, können Sie das Protokoll bei Bedarf anpassen.
Zum Synchronisieren zwischen der Master-Datenbank und der Client-Datenbank können Sie jedes Transportprotokoll verwenden, auf das Sie Zugriff haben, HTTP oder andere. Der wichtige Teil ist die Logik hinter der Synchronisierung. Für eine einfache Vorgehensweise bündeln Sie einfach alle letzten Änderungen, die der Client seit dem letzten Zeitstempel in der Client-Datenbank benötigt, vom Server. Wenden Sie es auf die Client-Datenbank an und fahren Sie damit fort. Wenn Sie auf Client-Seite weitere Änderungen vorgenommen haben, laden Sie diese hoch, falls sie noch relevant sind. Andernfalls verwerfen Sie sie.
Einige Spiele verwenden nicht einmal eine lokale Datenbank. Sie verfolgen den Status einfach, indem sie die relevanten Informationen vom Server bündeln, wenn sie benötigt werden.
Wenn es nicht akzeptabel ist, lokale Ereignisse zu verlieren, sollten Sie über lokalen Speicher verfügen und so oft wie möglich in diesem Speicher speichern. Sie können dies vor jedem Netzwerkversand versuchen.
Um nach aktiven Benutzern zu suchen, haben wir bei einem erfolgreichen Spiel alle 20 Sekunden mit HTTP gepingt ... Dieser Wert ist sofort gestiegen, da die Server überlastet wurden :( Das Serverteam hat nicht über den Erfolg nachgedacht. Ich möchte Sie also bitten, hinzuzufügen Eine Nachricht oder eine Art spezieller Header in Ihrem Kommunikationsprotokoll, mit dem Sie Ihre Clients neu konfigurieren können (für den Ping-Frequenz-Lastausgleich und andere kommunikationsbezogene Werte).
Clientseitige Überprüfungen sind ausreichend, wenn Sie nichts gegen Betrüger, Hacker und andere automatisierte Skripte haben, die Ihr Spiel angreifen. Der Server kann Ihre Aktion einfach ablehnen und eine Fehlermeldung mit einigen Details senden. Sie können diese Fehlermeldung beheben, indem Sie entweder die lokalen Änderungen rückgängig machen oder sie nicht auf Ihren lokalen Speicher anwenden, wenn Sie warten können, bis der Server antwortet, um die Änderungen tatsächlich zu speichern.
Wir haben das Befehlsmuster in unserem Spiel verwendet, um ein einfaches und effizientes Rollback fehlgeschlagener oder ungültiger Benutzeraktionen zu ermöglichen. Befehle wurden lokal gespielt, an die Peers gesendet oder in die Servernachricht übersetzt und dann auf die Peers angewendet oder auf dem Server überprüft. Im Falle eines Problems wurden die Befehle nicht abgespielt und die Spielszene kehrte mit einer Benachrichtigung in den Ausgangszustand zurück.
Die Verwendung von HTTP ist keine schlechte Idee, da Sie sich später sehr einfach in Flash- oder HTML5-Clients integrieren können. Es ist flexibel, Sie können jede Art von Serverskriptsprache verwenden und mit grundlegenden Lastausgleichstechniken später ohne großen Aufwand weitere Server hinzufügen um dein Backend zu skalieren.
Sie haben viel zu tun, aber es macht Spaß ... Also viel Spaß!
quelle
Am einfachsten ist es, die Datenbank als einzelne Datei zu implementieren, die Sie übertragen können. Wenn Sie versuchen, Unterschiede zwischen Datenbanken zu vergleichen, dann ist das eine Welt voller Schmerzen, und ich spreche aus Erfahrung.
Beachten Sie, dass Ihr Server Entscheidungen, die der Client basierend auf dieser lokalen Datenbank trifft, nicht vertrauen darf, da der Client sie ändern kann. Es muss nur für Präsentationsdetails existieren.
Was ist, wenn der Server entscheidet, dass das Ereignis nie stattgefunden hat? Der Client sollte sowieso keine Entscheidungen über Ereignisse treffen, da dem Client nicht vertraut werden kann.
Ich stelle fest, dass Sie auch auf zwei Arten über die lokale Datenbank sprechen: einmal für "Dinge, die sich nicht häufig ändern" und zweimal für Ereignisse. Diese sollten sich aus den oben genannten Gründen nicht wirklich in derselben Datenbank befinden. Sie möchten nicht versuchen, einzelne Datenzeilen in Datenbanken zusammenzuführen oder zu vergleichen. Die referenzielle Integrität wird beispielsweise zu einem Problem, wenn ein Client einen Verweis auf ein Element hat, das Sie vom Server entfernen möchten. Oder wenn der Client eine Zeile und der Server eine Zeile ändert, welche Änderung hat Vorrang und warum?
Ja, vorausgesetzt, sie sind eher selten oder klein. HTTP ist nicht bandbreiteneffizient, denken Sie daran.
Wenn Sie ein vorübergehendes Protokoll wie HTTP verwenden, ist dies eine vernünftige Idee. Wenn der Server eine Nachricht von einem Client empfängt, können Sie die "zuletzt gesehene" Zeit für diesen Client aktualisieren.
Nein überhaupt nicht. Der Kunde ist in den Händen des Feindes. Wie eine Aktion zurückgesetzt wird, hängt ganz davon ab, was Sie für eine Aktion halten und welche Auswirkungen sie haben könnte. Am einfachsten ist es, die Aktion erst dann zu implementieren, wenn der Server antwortet, um dies zuzulassen. Eine etwas schwierigere Methode besteht darin, sicherzustellen, dass jede Aktion über eine Rollback-Funktion verfügt, mit der die Aktion rückgängig gemacht und alle nicht bestätigten Aktionen auf dem Client zwischengespeichert werden können. Wenn der Server sie bestätigt, löschen Sie sie aus dem Cache. Wenn eine Aktion abgelehnt wird, setzen Sie jede Aktion in umgekehrter Reihenfolge bis einschließlich der abgelehnten zurück.
quelle