# Komplexes Netzwerk-Spiel im Internet



## JackDaniels86 (9. Feb 2012)

Hallo zusammen!

Ich arbeite in einer Gruppe an einem relativ umfangreichen Spiele-Projekt und bin hier für den Online-Part zuständig.

Hier erstmal ein paar grundlegende Informationen:


Generelle Umsetzung mit MVC-Modell
Es wird einen Offline-/Singleplayer-Modus mit entsprechenden Controller-Klassen geben.
Eigener Server ist vorhanden. Persistente Daten sollen in MySQL-Datenbanken gespeichert werden.
Spieler sollen über eine Lobby ein Online-Spiel mit einem anderen Spieler beginnen können.
Im laufenden Online-Spiel soll eine Chat-Funktion integriert sein
Es handelt sich nicht um ein Runden-basiertes Spiel. Dass beide Spieler also auf dem möglichst gleichem Stand sind, was die Ansicht angeht, ist ziemlich wichtig
Da das Online-System mit einer Rangliste fungiert, soll eine Manipulation (Cheaten) der Spieler möglichst verhindert werden
Vor dem Betreten der Lobby, muss sich der Spieler auf dem Server anmelden(es muss also zuvor ein Account angelegt werden)
Sämtliche virtuelle Besitztümer des Spielers im Spiel werden ausschließlich auf dem Server gespeichert. Online- und Offline-Spiel sind somit strikt von einander getrennt.

Ich möchte gerne von Anfang an die ganze Client-Server-Architektur möglichst "professionell" aufbauen, um Sicherheit(Account-Hack, Cheat, Abfangen von Datenströmen) und Zuverlässigkeit(möglichst viele Online-Spiele) auch bei größerer Nachfrage zu gewährleisten.

Die entscheidenden Fragen liegen hier eher bei der gesamten Architektur, als bei der Implementierung.

Meine Ansätze:

*Verbindung:*
Auf dem Server existiert eine MainServer-Klasse, die an einem Port lauscht und ankommende Anfragen an ConnectionHandler(Thread) weiterleitet.
D.h. jeder Client ist einem eigenen ConnectionHandler zugewiesen, der mit dem Clienten kommuniziert.
Der ConnectionHandler ist auch für das Login-Verfahren des Spielers verantwortlich.
Solange der Spieler nicht erfolgreich eingeloggt ist, nimmt der ConnectionHandler keine Anfragen außer "Einloggen" und "Verbindung Trennen" entgegen.
Haben zwei Spieler ein Spiel gestartet, werden die jeweiligen ConnectionHandler einem MatchHandler(Thread) übergeben, der dann die direkte Kommunikation zwischen den Spielern ermöglicht.


*Kommunikation:*
Die Verbindung wäre somit eine TCP-Verbindung zwischen Client und Server.
Die komplette Spiellogik (Controller-Klassen) findet auf dem Server statt um Manipulationen vorzubeugen.
Macht der Spieler bei sich also eine Eingabe, sendet der Client einen Stream an den Server.
Dieser würde dann aus einem "Anfangscode" und dem generellen Inhalt bestehen.
Der Anfangscode dient dem Server zur Identifizierung der Anfrage(Ist dieser Stream eine Nachricht? Oder ein Spielereignis? Etc.). 
Der dahinter folgende Inhalt des Streams wäre dann der "Methodenaufruf" inkl. Parameter.
Hier habe ich mir überlegt Hexadezmalwerte zu verschicken, die der Server eindeutig einer Methode zuweist und diese dann mit dem angehängtem Parameter aufruft.

Beispiel: $TYP$METHODE$PARAMETER1$PARAMETER2$...

Das gleiche Prinzip wird auch für Nachrichten vom Server an den Client genutzt.

Drück also der Spieler einen Knopf, wertet der Server über die Controller-Klassen die Aktion aus und schickt Änderungen an beide Spieler zurück, damit diese ihre Ansicht aktualisieren.
Das Spiel läuft somit komplett Server-seitig. Der Client dient im Grunde nur noch zur Anzeige und Nutzereingabe. 


*Sicherheit:*
Um Manipulationen am Online-Spiel(offline ist dies egal), d.h. Spielverlauf und Daten zu verhindern habe ich mir ebenfalls Gedanken gemacht.
Die Verbindung mit dem Server wird mit SSL aufgebaut, d.h. die Kommunikation verschlüsselt.
Bei jeder Anfrage schickt der Client den Hashcode des gesamten Programms mit. Dieser wird beim Starten des Spieles einmal berechnet. Jede vorhandene Klasse gibt ihren eigenen Hashcode wieder und diese werden in einer speziellen Klasse zusammengefügt.
Ich als Entwickler kenne alle Versionen meines Spieles und somit auch alle Hashcodes und könnte diese auf dem Server vergleichen.
Wenn ein nicht bekannter Hashcode vom Client kommt, kann ich also davon ausgehen, dass das Programm manipuliert wurde oder eine nicht mehr zulässige Version des Spieles nutzt und somit jede weitere Anfrage abblocken.
Des weiteren wird immer überprüft ob der Spieler noch eingeloggt ist. Ein Spieler kann sich nur "einmal gleichzeitig" einloggen, was eine Manipulation durch mehrere Spiele-Instanzen verhindern soll.



Leistet dieses Modell die von mir geforderte Sicherheit und Stabilität?
Wären andere Lösungsansätze effizienter?
Was muss ich noch beachten?

Ich hoffe mein Anliegen ist einigermaßen deutlich geworden.
Wenn nicht, einfach nachhaken! 

Schon mal vielen Dank für eure Hilfe!


----------



## tuxedo (9. Feb 2012)

Da du es "professionell" aufziehen willst:

Zum Thema Verbindung:

* Netty
* MINA
oder sonst ein Framework das die den Verbindungskrempel abnimmt und dich beim entwickeln des benötigten Protokolls unterstützt.

Zum Thema Kommunikation:

TCP oder UDP... Kommt drauf an wie echtzeit das ganze laufen soll. Die Geschwindigkeit eines Ego-Shooters erreichst du mit TCP eher weniger. Die Geschwindigkeit von einem MMORPG: Da reicht auch TCP only.



> Hier habe ich mir überlegt Hexadezmalwerte zu verschicken, die der Server eindeutig einer Methode zuweist und diese dann mit dem angehängtem Parameter aufruft.



Wieso willst du das Rad neu erfinden? Das gibt's schon. Nennt sich "Remote Procedure Call" und wurde mit RMI, SIMON und anderen Libs schon mehrfach implementiert. Was würde deine implementierung so besonders gegenüber den anderen machen?

Vielleicht schaust du erst mal wie andere vergleichbare Spiele ihr Protokoll aufgebaut haben. Bei World of Warcraft kannst du hier schauen: mangos foundation &mdash; the free, open source World of Warcraft server (C++) Bei Lineage2 kannst du hier abschauen: L2j Server Project (Java)
Gibt noch andere frei verfügbare Server wo man gucken könnte.

Zum Thema Sicherheit:



> Um Manipulationen am Online-Spiel(offline ist dies egal), d.h. Spielverlauf und Daten zu verhindern habe ich mir ebenfalls Gedanken gemacht.
> Die Verbindung mit dem Server wird mit SSH aufgebaut, d.h. die Kommunikation verschlüsselt.



Sorry, aber professionell wirkt es nicht wenn du SSH mit SSL verwechselst.
Und wenn ich mich recht erinnere, dann verhindert SSL nur, dass Dritte in die Verbindung eingreifen können. Wer aber Server oder Client kompromittiert, kann die Verbindung manipulieren. 
WoW z.B. setzt auf der TCP Verbindung auf Protokollebene eine eigene Verschlüsselung ein. Die ist zwar nicht schwer zu knacken, aber im Protokoll werden die Nachrichten mit sogenannten OP-Codes identifiziert. Nur wenn Server und Client das gleiche Verständnis von OP-Codes haben, funktioniert die Kommunikation. Wenn man genug Zeit hat, findet man auch heraus welcher OP-Code was bedeutet und "könnte" die Daten manipulieren. Aber der Hersteller ist dazu über gegangen regelmäßig die OP-Codes neu durchzuwürfeln. Damit geht die Suche nach "Welcher OP-Code bedeutet jetzt was" ständig von vorne los. 

Alles in allem: Schau was andere fabriziert haben und lerne daraus...


----------



## Marcinek (9. Feb 2012)

JackDaniels86 hat gesagt.:


> Leistet dieses Modell die von mir geforderte Sicherheit und Stabilität?
> Wären andere Lösungsansätze effizienter?
> Was muss ich noch beachten



Das sind komplexe Fragestellungen, die ohne eine Codereview nicht 100 % beantwortet werden können.

Wenn du einen Server implementierst, dann musst du Dir gedanken über die Skalierbarkeit machen. Was passiert bei 10 Spielern und was bei 10000.

Jedes Java Programm kann ich manipulieren und er schickt immernoch korrekte Hashwerte zurück. 

SSL schützt dich vor man in the middle Attacken. Aber wenn ich der Client bin, dann kann ich alles schicken, was ich will.

Hört sich viel nach "Das Rat neu entwickeln" an. Das ist in der Softwarebanrche hochgradig ineffizient. Hierfür gibt es bestimmt schon Frameworks, die entsprechende Kommunikation machen.


----------



## JackDaniels86 (9. Feb 2012)

Erstmal danke für eure Antworten!

Ich meinte natürlich SSL und nicht SSH! Bin noch ein wenig müde.^^

Ich habe noch eine wichtige Sache vergessen zu erwähnen.
Der Server soll evtl. in Zukunft auch mit Clienten kommunizieren können, die nicht in Java geschrieben sind.
Würde aus diesem Grund RPC nicht wegfallen?

Ich möchte das Rad auf keinen Fall neu erfinden!

Falls meine Probleme bereits gelöst wurden wie ich es brauche, würde ich natürlich auf bestehende Lösungen zurückgreifen.
Nur leider habe ich hier keine Ahnung.

Ich werde jetzt erstmal dem Rat folgen und schauen was "andere fabriziert haben". 

Falls aber noch spontane Vorschläge vorhanden sind, immer her damit!


----------



## tuxedo (9. Feb 2012)

Mit RPC ist es nicht unmöglich sprachübergreifend zu kommunizieren. Aber einfach ist das wirklich nicht. Würde RPC hier echt nicht nehmen, sondern auf ein anständiges, fundiertes Protokoll aufsetzen. Damit's skalierbar ist würde ich zu MINA oder Netty greifen. Oder mit Java 7 AIO das ganze selbst machen. Aber die genannten Frameworks sind langerprobt, performant und unterstützen dich prima bei der implementierung eines eigenen Protokolls.

- Alex


----------



## c_sidi90 (9. Feb 2012)

Wir sind momentan auch dabei ein großes C++ Projekt (Spiel) nach Java zu portieren und haben uns MINA und NETTY zu Beginn angeschaut und getestet. Unsere Wahl viel im Endeffekt auf NETTY. Begründung: Bessere Dokumentation, guter Support, besser zu handeln und weniger Code zu schreiben als mit MINA.


----------



## JackDaniels86 (9. Feb 2012)

Interessant.
Habt ihr zufällig ein paar Tutorials für Mina un Netty bereit?
Habe dazu leider nix brauchbares im Netz gefunden.


----------



## Evil-Devil (9. Feb 2012)

Auf AltDevBlogADay gibt es zwei interessante Artikel zu RMI über Websockets.

Das könnte man evtl. in der ein oder anderen Variante sicher auch Cross Plattform fähig bekommen.

#AltDevBlogADay  Writing Your Own WebSocket Server
#AltDevBlogADay  Controlling Your Game Engine Over WebSocket

Als Grundidee jedenfalls cool


----------



## dayaftereh (9. Feb 2012)

Auf der Netty Seite selbst sind Beispiele und die Doku beschreibt die Wichtigsten Klassen sehr ausführlich.


----------



## tuxedo (9. Feb 2012)

Evil-Devil hat gesagt.:


> Auf AltDevBlogADay gibt es zwei interessante Artikel zu RMI über Websockets.
> 
> Das könnte man evtl. in der ein oder anderen Variante sicher auch Cross Plattform fähig bekommen.



Oh man.. Wieso wird hier alles verdreht? Du meinst RPC, nicht RMI.

RPC == Die Technik des "entfernten Methodenaufrufs"
RMI == Remote Method Invocation == Eine konkrete RPC Implementierung für/in Java.

Da ist wie gesagt nicht viel mit Sprachunabhängigkeit (mit dem IIOP Protokoll für RMI kannst du ggf. über Corba mit anderen Sprachen sprechen: IIOP ? Wikipedia. Aber wer will schon freiwillig Corba?!). Cross-Plattform: Ja, solange es Java bleibt. 

- Alex


----------



## Evil-Devil (10. Feb 2012)

Ich weiß das ich RPC meinte. In den Links geht es gerade darum nicht RMI für RPC zu nutzen sondern einen eigenen schlankeren Weg einzuschlagen. Zb. über Websockets via JSON. Und das wäre dann nahezu Sprach/System unabhängig.

Für die direkte Kommunikation zwischen Client und Server würde ich aber die schon angesprochenen OpCodes empfehlen.

Will hier keinen Streit vom Zaun brechen ^^"


----------



## Empire Phoenix (12. Feb 2012)

Also gerade bei shooter hnlcihen spielen macht es sinn die Netzwerkebene auf nio der aio ebene selber zu schreiben, da man dann auf bytebuffer ebene wesentlich effizienter daten transferieren kann (zb Messages je nach typ über tcp oder über udp übertragen, inclusive fallback auf tcp only wenn udp nicht durch die firewall kommt) Mit aio habe ich bislang ncihts gemacht, aber gerade bei nio kann man eine toSend queue nehmen, und gut anhand der anzahl der objecte darinnen andere paramter autoamtisch anpassen (zb bei clients mit langsamen internet die world update rate runtersetzten und mehr mit interpolation arbeiten)


----------



## ice-breaker (12. Feb 2012)

Empire Phoenix, das gleiche kannst du aber auch auf Netty-Basis machen, du musst dann eben nur noch einen Layer über Netty drübersetzen der dann die NAchrichten auf  TCP oder UDP verteilt. Auch mit dem Zählen von Objekten in der Queue könnte man da einiges bewirken, eine shared-Variable für die Anzahl an Objekte im Send-Buffer, wird das Objekt durch den Netty-Handler in die Byte-Daten transformiert, dann dekrementierst du den Zähler eben.
Das ganze mag nicht so genau sein, wie alles selbst zu bauen, birgt jedoch auch viel weniger Probleme, denn fertige Frameworks sind seit Jahren erprobt und in tausenden Deployments im Einsatz, da wird weit weniger ein schwerer Fehler drinne sein, als wenn man versucht das Rad neu zu erfinden und selbst ein NIO-Framework schreibt.


----------



## Massenhaft (21. Feb 2012)

Hab mal mit Netty/UDP ein "2d Shooter" gebaut...hier der Link:
slick-netty-network-game-example - An UDP 2d netwok game example - Google Project Hosting

vielleicht hilfts 

Gruß
Andreas


----------

