Entwicklung einer Massive Multiplayer Network Engine in Java

Transcrição

Entwicklung einer Massive Multiplayer Network Engine in Java
Entwicklung einer
Massive Multiplayer
Network Engine
in Java
Diplomarbeit
Vorgelegt von
Christian Stein
Institut für Computervisualistik
Arbeitsgruppe Computergraphik
Betreuer und Prüfer: Prof. Dr.-Ing. Stefan Müller
Zweitprüfer: Prof. Dr. Christoph Steigner
Oktober 2004
Hiermit erkläre ich, dass ich diese Diplomarbeit selbständig und nur mit den
angegebenen Hilfsmitteln angefertigt habe.
Koblenz, den 28. Oktober 2004
Christian Stein
Blatt mit der Aufgabenstellung statt dieser Seite einfügen!
⇐
TODO
Zusammenfassung
In dieser Arbeit wird eine allgemeine Network Engine für Massive Multiplayer
Onlinespiele in Java entwickelt und für die Verwaltung großer isometrische
Landschaften optimiert. Anhand des Spiels Run To The Hills werden die
Hauptfunktionen der Engine demonstriert.
Die Basis der Engine bildet das klassische Client-Server Modell. Diese
zentralisierte Architektur bietet neben der einfacheren Umsetzungsmöglichkeit auch hinsichtlich der Synchronisierung vieler Clients eindeutige Vorteile
gegenüber dezentralen oder replizierenden Modellen. Das ermöglicht einen
unkomplizierten Beitritt eines neuen Clients in ein laufendes System.
Serverseitig kompensieren drei Schichten den durch das gewählte Modell
unabdingbaren Mehraufwand an Kommunikations- und Prozessorbelastung.
Die Aufteilung dieser Last passiert für den Spieleentwickler völlig transparent und bedarf keiner aktiven Programmierung seinerseits. Administratoren können jedoch zur Laufzeit dem System weitere Resourcen hinzufügen
(oder welche entfernen) um das Servernetzwerk an erhöhte (oder geringere)
Anforderungen anzupassen. Eine webbasierte und skriptbare Schnittstelle ermöglicht ausserdem ein Eingreifen in das Spielgeschehen. Der Einsatz nicht
blockierender Sockets sichert die hohe Leistungsfähigkeit der Datenübertragung bei vielen Netzwerkverbindungen.
Die Verwendung der Programmiersprache Java bietet erstens einen Zusammenschluss heterogener Hardware- und Betriebssysteme auf Seiten der
Serverkomponenten und wahrt zweitens die Plattformunabhängikeit des Endnutzers, dem Spieler. Damit braucht der Spieleentwickler auch clientseitig keine Portierung des Spiels vorzunehmen und erreicht automatisch eine größere
Zielgruppe.
Diese Arbeit entstand in Zusammenarbeit mit Thomas Schuster, der im
Rahmen seiner Diplomarbeit Entwicklung einer isometrischen Grafik-Engine
in Java die graphischen Grundlagen von Run To The Hills implementierte.
iii
Inhaltsverzeichnis
1 Einleitung
1.1 Verspielte Einleitung . . . . . . . . . . .
1.1.1 Spiele . . . . . . . . . . . . . . .
1.1.2 Computerspiele . . . . . . . . . .
1.1.3 Multiplayer Computerspiele . . .
1.1.4 Multiplayer Onlinespiele . . . . .
1.1.5 Massive Multiplayer Onlinespiele
1.1.6 Zusammenfassung . . . . . . . . .
1.2 Übersicht der Arbeit . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
1
1
1
2
3
4
4
5
5
2 Grundlagen, Systeme und Verfahren
2.1 Systeme . . . . . . . . . . . . . . . .
2.1.1 Echtzeitsysteme . . . . . . . .
2.1.2 Verteilte Systeme . . . . . . .
2.2 Technische Grundlagen . . . . . . . .
2.2.1 Simulationen . . . . . . . . .
2.2.2 Resourcemanagement . . . . .
2.3 Bestehende Lösungen . . . . . . . . .
2.3.1 Java Shared Data Toolkit . .
2.3.2 OpenSkies . . . . . . . . . . .
2.3.3 Terazona . . . . . . . . . . . .
2.3.4 Sun Game Server Technology
2.3.5 Zusammenfassung . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
6
6
7
9
9
10
11
12
12
12
14
16
18
3 Anforderungsanalyse
3.1 Anforderungen an das System . . . . . . . . . . . . . . . . . .
3.2 Anforderung an den Client . . . . . . . . . . . . . . . . . . . .
3.3 Anforderungen an den Server . . . . . . . . . . . . . . . . . .
19
19
20
21
iv
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
4 Design
4.1 Mamuneen . . . . . . . . . . .
4.2 Drop’n Pick! . . . . . . . . . .
4.2.1 DropItemCommand . .
4.2.2 PickItemQuery . . . .
4.2.3 Zusammenfassung . . .
4.3 Late-Join . . . . . . . . . . .
4.3.1 Late-Join eines Clients
4.4 Komponenten . . . . . . . . .
4.4.1 Command . . . . . . .
4.4.2 Application . . . . . .
.
.
.
.
.
.
.
.
.
.
23
23
24
25
27
29
30
30
31
31
32
5 Implementierung
5.1 Externe Bibliotheken . . . . . . . . . . . . . . . . . . . . . . .
5.1.1 Logging . . . . . . . . . . . . . . . . . . . . . . . . . .
5.1.2 Webadmin . . . . . . . . . . . . . . . . . . . . . . . . .
5.2 Umsetzung . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5.2.1 CAPI: Die Command API . . . . . . . . . . . . . . . .
5.2.2 Binden von Kommandoklassen und execute-Methoden
5.2.3 Java NIO Selector . . . . . . . . . . . . . . . . . . . . .
35
35
35
36
38
38
40
43
6 Ein Demospiel
6.1 Design . . . . . . . . . . . .
6.1.1 Allgemein . . . . . .
6.1.2 Anforderungsanalyse
6.2 Implementierung . . . . . .
6.3 Screenshots . . . . . . . . .
45
45
45
46
49
50
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
7 Ergebnisse
54
7.1 Serveradministration via Webbrowser . . . . . . . . . . . . . . 56
7.2 NIO: Fehler in Sun JDK 1.4 für Windows . . . . . . . . . . . . 59
7.3 Fazit und Ausblick . . . . . . . . . . . . . . . . . . . . . . . . 61
Abkürzungsverzeichnis
FAQ
GPRS
HTTP
LAN
LRMP
MOG
MMOG
MVC
NAT
NIO
OSI
P2P
RTCP
RTP
SoC
SSL
TCP
UDP
Frequently Asked Questions (. . . and answers)
General Packet Radio Service
Hypertext Transfer Protocol
Local Area Network
Light-weight Reliable Multicast Protocol
Multiplayer Online Game
Massive Multiplayer Online Game
Model–View–Controller Designpattern
Network Address Translation (auch: masquerading)
New Input/Output Paket von Java
Open Systems Interconnection
Peer–to–Peer Netzwerk
Real–Time Transfer Control Protocol
Real–Time Transfer Protocol
System on Chip
Secure Socket Layer
Transmission Control Protocol
User Datagram Protocol
vi
Kapitel 1
Einleitung
Den Anfang macht das Wort Spiel, gefolgt von Computerspiel und Multiplayer Computerspiel. Danach wird das Wort Computer durch Online ersetzt
und abschließend noch den Massen an Spielern durch das Wort Massive genüge getan. Aus dieser naiven Betrachtungsweise lassen sich jedoch bereits
grundlegende und gemeinsamen Eigenschaften von Spielen im allgemeinen
ableiten, die sich mehr oder minder stark in allen Spielformen wiederfinden.
1.1
1.1.1
Verspielte Einleitung
Spiele
Reale Spiele machen Spaß! Dieser Spaß ist eine Motivation für die Entwicklung von immer neuen und Veränderung bekannter Spielideen.
Es sind viele Möglichkeiten zur Klassifizierung von Spielen denkbar. Intuitiv kann zum Beispiel zwischen Gesellschaftsspielen, Denkspielen und Sportspielen unterschieden werden. Weiterhin ist die Spieldauer (festgelegt oder
variabel) als Kriterium denkbar. Auch die Gruppen- oder Mannschaftsbildung (Jeder gegen jeden oder klare Teams) und Rollenverteilung (Torwart
und Feldspieler oder gleichberechtige Spieler im Tennisdoppel) können als
Unterscheidungsmerkmale herangezogen werden.
Im Rahmen dieser Arbeit interessieren hauptsächlich
• die Anzahl der Spieler
• und die Art und Weise wie ein Spieler mit dem Spiel (und den Mitspieler) interagiert oder interagieren darf.
Die Anzahl der Spieler lässt sich meist aus dem Regelwerk des Spiels entnehmen und ist in vielen Fällen explizit vorgeschrieben. Zum Beispiel werden
1
KAPITEL 1. EINLEITUNG
2
für eine ordentliche Skatrunde genau drei Brüder benötigt, für eine Partie
Schach genau zwei.
Bei realen Spielen ist die Mindestanzahl immer eins oder höher. Computerspiele können auch ohne Spieler auskommen. Spiele können eine maximale
Anzahl an Spielern festlegen oder durch den Raum des Spielfeldes begrenzt
sein. Die meisten Gesellschaftsspiele, darunter fallen zum Beispiel Brett-,
Karten- und Würfelspiele, laufen zugweise ab. Zu einem fixen Zeitpunkt ist
genau ein Spieler am Zug – der Rest schaut zu und wartet unter Umständen
stundenlang bis der eine Spieler zieht. Eine dem Autor bekannte Ausnahme
ist das Kartenspiel Ligretto. Hier können alle Spieler am Tisch gleichzeitig
Aktionen ausführen und ihren Kartenstapel abarbeiten ohne auf die Beendigung des Zuges eines Vorgängers zu warten. Auch Sportspiele, vor allem
Mannschaftspiele, funktionieren meist hochgradig konkurrent:
Jeder agiert und reagiert zu jedem Zeitpunkt der Spielzeit und
versucht das Spielziel zu erreichen oder die Spielsituation zu seinen Gunsten zu verbessern.
Dieser Aspekt der gleichzeitigen und konkurierenden Interaktionsmöglichkeit
ist der Kerngedanke von Massive Multiplayer Online Games. Er stellt ebenfalls die Hauptmotivation der teilnehmenden Spieler dar.
1.1.2
Computerspiele
Die Faszination von Computerspielen ist unumstritten. Ihre Geschichte begann 1962 als zwei der Mitarbeiter des MIT, Steve Russel und Dan Ewards für
die eben angelieferte PDP-1, den ersten Computer mit Tastatur und Kathodenstrahlbildschirm das erste Programm entwickelten - ein Computerspiel.
Sie nannten es Spacewar und es war ein Shoot-Up Game, das es erlaubte, am
Bildschirm Raumschiffe abzuschießen.
In den darauffolgenden Jahren entstanden zumeist in BASIC programmierte Spiele (wie Lunar Lander, ein textbasiertes Simulationsspiel oder
Kingdom, eine Simulation von ökonomischen Prozessen und Vorgänger von
Sim City).
1972 gründeten Nolan Bushnell und Al Alcorn mit einem Startkapital von 500 Dollar das Unternehmen Atari. Sie entwickelten den ersten
Computerspiel-Automaten Pong, der zum Schlager für Arcade Games wurde
und 1974 als Spiel für Heimcomputer auf den Markt gebracht wurde. Es besaß nur zwei Bildschirmausgaben: Deposit Quarter und Avoid missing Ball
for High Score. Ein großer Verkaufserfolg für Atari wurde 1978 VCS 2600
KAPITEL 1. EINLEITUNG
3
als hybride Station aus Fernseher, Videospiel und Computer, ein Jahr später gründeten vier ehemalige Atari Mitarbeiter die erste Spielesoftwarefirma
Activision.
1980 veröffentlichte Nintendo Pac Man, das sowohl in den Game Arcades
als auch am Heimcomputermarkt ein Langzeiterfolg wurde. In den nächsten
Jahren deutete sich ein großer finanzieller Einbruch des Spielesektors ab, der
erst Ende der achtziger Jahre mit dem Commodore Amiga und dem Gameboy
von Nintendo wieder in Schwung kam. Die bekanntesten Spiele waren damals
Sim City für Amiga und Tetris für den Gameboy. Nintendos Super Mario
Brothers, das 1990 herauskam, spielte weltweit 500 Millionen Dollar ein.
Das populärste First Person Shooter Spiel wurde 1993 Doom. Ebenso
großen Erfolg hatte Quake, das Nachfolgespiel, das drei Jahre später auf
den Markt kam. 1998 stellte Epic Megagames Unreal 1 vor. Dieses Spiel
ermöglichte es dem Benutzer erstmals mit Hilfe eines Level Editors selbst
Spielwelten zu erzeugen. Mit der Entwicklung des japanischen Spieles Final
Fantasy VIII im Jahr 1999, bei der 400 Künstler beschäftigt waren, wurde
erstmalig für die Entwicklung eines Computerspiels ein Budget eingerichtet,
das diejenigen großer Hollywood Filmproduktionen überstieg.
Zwar können auch Denk- und Einzelspielerspiele wie Solitär, Tetris und
Co stundenlangen Spaß garantieren, doch ist das unmittelbare Kräftemessen
mit andere Menschen noch reizvoller. Zusätzlich zum klassischen Gegner Zeit,
kommt beim Gegeneinanderspielen der Anreiz des direkten Vergleichs hinzu.
1.1.3
Multiplayer Computerspiele
Eine der ersten Varianten der multiplayer computer games (MCG) ist die,
bei der mehrere Spieler gleichzeitig an einem Computer und den daran angeschlossenen Eingabegeräten wie Tastatur, Maus, Joysticks oder -pads zusammen oder gegeneinander spielen. Der Vorteil dieser Art der Unterstützung mehrerer Spieler liegt im einfachsten Fall in der Zuordnung von einem
bestimmten Eingabegerät zu einem bestimmten Spieler.
Je nach Spielart kann die gemeinsame Sicht auf das Spiel jedoch zu Problemen führen. Vor allem, wenn sich die Spieler im virtuellen Raum örtlich
voneinander entfernen können. Entweder muss die Bewegungsfreiheit eingeschränkt werden oder es wird die zur Verfügung stehende Bildschirmfläche
gemäß der Spieleranzahl aufgeteilt. Jeder Bildschirmausschnitt kann somit
eine individuelle Ansicht präsentieren. Ein weiteres Problem dieser Variante
ist, dass es gegenüber dem Mitspieler keine Geheimnisse geben kann. Man
spielt immer mit offenen Karten – was wiederum eine Menge von Spielformen
ausschließt.
KAPITEL 1. EINLEITUNG
4
Durch den Einsatz eines Modems wird nicht nur die Einschränkung auf
offene Spielformen festgelegt zu sein überwunden. Zusätzlich kann sich der
Spielpartner auch an einem beliebigen Ort mit einem Telefonanschluss befinden. Die lokale Version der Modemvariante bei der die Rechner nur einige
Meter von einander entfernt stehen, bietet sich durch die Verwendung eines
Nullmodemkabels. Mit diesem Kabel wird mithilfe von Software ein Modem
simuliert und verbindet somit zwei Rechner ohne Onlinekosten. Die laufenden Kosten sind ein stark hemmender Faktor für die Ausbreitung von MCGs.
Der Vorteil gegenüber der eingangs beschriebenen Variante ist die getrennte Sicht der beiden Spieler auf das Spiel. Allerdings ergibt sich durch diese
Trennung ein Synchronisationsaufwand zwischen den beiden Spielen.
Doom erlaubte 1993 als eins der ersten Spiele, dass sich mehrere Spieler,
die jeweils an einem eigenen Rechner agieren, gleichzeitig in einer simulierten Welte befinden. Die Vernetzung der Rechner geschah damals über ein
Intranet, auf dem meist das IPX-Protokoll von Novell gefahren wurde.
1.1.4
Multiplayer Onlinespiele
Unter online sein versteht man heutzutage das verbunden sein mit dem Internet. Multiplayer online games sind demnach Spiele, die erst funktionieren,
wenn man seinen Computer dem weltweiten Verbund aus Rechnern hinzufügt
und dort nach Mitspielern sucht.
Die einzige Weiterentwicklung bezüglich der MCGs liegt im Austausch
des Datentransferprotokolls. Die IPv4-Suite löste IPX weitgehend ab und
behauptet sich seit Jahren gegen die Ablösung durch den designierten Nachfolger IPv6.
Mit den sinkenden Onlinekosten und stärkeren Ausbreitung der breitbandigen Zugänge zum Internet wuchs auch der Absatzmarkt für MOGs. Auch
derzeit ist dieses Wachstum noch nicht abgeschlossen. Die Zeichen für eine
Entwicklung hin zu einer größeren Anzahl von Spieler, die an einem Spiel teilnehmen, stehen sogar sehr gut. Dieses soll im nächsten Abschnitt erläutert
werden.
1.1.5
Massive Multiplayer Onlinespiele
Wie beim Übergang vom MCG zum MOG ist auch der nächste Schritt vom
MOG zum massive(ly) multiplayer online game lediglich eine konsequente Weiterentwicklung von Mehrbenutzer-Computersimulationen. Die einzige
Neuerung, und damit Abgrenzungskriterium zu MOGs, ist die Verwaltung
von bis zu drei Größenordnungen höhere Anzahl an Spielern. Statt zwei bis
KAPITEL 1. EINLEITUNG
5
vier Spieler geht es bei MMOGs um 200 bis 400, in einigen Systemen interagieren bereits mehr als 1000 Spieler gleichzeitig. Ein Mitarbeiter der Firma
Blizzard[Bliz], Chris Sigaty, beziffert die Anzahl der auf einem Server gleichzeitig aktiven Spieler auf 2500 bis 3000 [Gamo].
Wie schon beim einfachen Zweispieler-Spiel gibt es natürlich auch hier immer noch die Unterscheidung zwischen zugbasierten und in gefühlter Echtzeit
ablaufenden Spielen. Erste sind durch ihre einfachere Umsetzbarkeit schon
länger auf dem Markt und verwenden Webseiten, Email und andere Internetdienste als Spielplattform und nutzen sie als Interaktions- beziehungweise
Informationsträger.
1.1.6
Zusammenfassung
Man erkennt spielend leicht, dass MMOGs die Tradition der Computerspiele
fortführen und dabei hohe Anforderung an die logistische und technische
Umsetzung stellen. Die in Echtzeit ablaufenden Spiele sind das Thema der
vorliegenden Diplomarbeit.
1.2
Übersicht der Arbeit
Nach der Einleitung und Klärung des Arbeitsauftrags folgt im nächsten Kapitel 2 eine Vorstellung einiger bereits bestehender Multiplayer-Systeme. Aus
dem Diskurs dieser Systeme im Kapitel 3, den Grundlagen von Echtzeitsystemen und der Motivation der echten Spiele, wird im Kapitel 4 ein Modell einer
Massive Multiplayer Engine beschrieben und auf den Namen Mamuneen getauft. Die prototypische Umsetzung befindet sich auszugsweise im Kapitel 5 –
gefolgt vom Bericht des Feldversuchs im Kapitel 6: Run To The Hills, dass
als proof-of-concept einen Teil dieser Arbeit ausmacht. Abschließend werden
die Ergebnisse im Kapitel 7 zusammengetragen.
Kapitel 2
Grundlagen, Systeme und
Verfahren
In diesem Kapitel wird das technische Umfeld der Arbeit vorgestellt. Die Erläuterung der Begriffe Echtzeitsysteme und Netzwerke stellt die Grundlage
für den Abschnitt Verteilte Systeme. Der Fokus liegt auf dem Client-Server
Modell, da andere Topologien (Strukturen) verteilter Anwendungen im Zusammenhang mit hochgradig interaktiven Onlinespielen in Bezug auf Schummeln nicht viel Sinn machen. Da der Flaschenhals im Client–Server Modell
die Leistungsfähigkeit des Servers ist, wird eine ereignisgesteuerte Architektur gewählt und genauer erklärt. Abschließend werden bestehende Verfahren
vorgestellt.
2.1
Systeme
Vorweg werden noch einige Schlagworte rund um Systeme genannt und für
die weitere Verwendung in diesem Dokument beschrieben. Es handelt sich
dabei nicht um eine vollständige Liste von allgemeinen Systemeigenschaften,
sondern um eine an den Inhalt der Arbeit angepasste Auswahl:
• Verfügbarkeit bedeutet, dass ein System für die bestimmungsgemäßen Aufgaben zur Verfügung steht.
• Der Begriff der Verlässlichkeit erweitert die Verfügbarkeit um die
Dimension der jeweils aktuell aufgabenbezogenen Nutzbarkeit von Ergebnissen aus bzw. in einem System. Ein verlässliches System muss
sicherstellen, dass es immer korrekte Ergebnisse erzeugt oder darüber
informiert, wenn dies nicht möglich war (Ausnahmefälle).
6
KAPITEL 2. GRUNDLAGEN, SYSTEME UND VERFAHREN
7
• Sicherheit bewertet Systemzustände auf ihre Auswirkungen hinsichtlich des Systems selbst und im allgemeinen Fall auch bezüglich seiner
Umwelt einschließlich der dort vorhandenen Menschen. Sicherheit ist
eine Sachlage (ein Zustand), bei der das durch Eintrittswahrscheinlichkeit und Schadensausmaß beschriebenes Risiko nicht größer ist als das
größte noch vertretbare Risiko. Letzteres wird auch Rest– oder Grenzrisiko genannt.
2.1.1
Echtzeitsysteme
Rechtzeitigkeit ist der einzig wirklich wichtige Aspekt von Echtzeitsystemen.
Solche Systeme reagieren auf externe Eingaben, welche auf vorhersehbare
Art und Weise allerdings zu unbekannten Zeitpunkten eintreffen können. Sie
verarbeiten diese Eingaben, treffen“ Entscheidungen und generieren, falls
”
notwendig, Ausgaben. Die Frage Wie?“ die Ein- und Ausgaben zu bzw. von
”
den System gelangen wird später in diesem Abschnitt erläutert. Neben der
aus [RT98] entnommenen kanonischen Definition eines Echtzeitsystems von
Donald Gillies:
1. Ein Echtzeitsystem ist ein Informationssystem, dessen Eigenschaften nicht nur von der logischen Ausgabe der Algorithmen bestimmt werden, sondern auch vom Zeitpunkt der Ausgabe. Wird
die Anforderung an die Rechtzeitigkeit nicht eingehalten, spricht
man von einem Systemfehler.
sind laut dieser FAQ auch folgende Auslegungen des Begriffs möglich:
2. Der POSIX Standard 1003.1 definiert Echtzeit für Betriebssystem als die Fähigkeit benötigte Dienste innerhalb einer beschränkten Antwortzeit zu liefern.
3. Manchmal werden schnelle Systeme auch als Echtzeitsysteme
bezeichnet. Es sei hier nochmal darauf hingewiesen, das Echtzeit
nicht zwangsläufig ein Synonym für Schnelligkeit steht; es ist eben
nicht alleine die Antwortzeit an sich ausschlaggebend (diese kann
sich im Rahmen von Sekunden bewegen), sondern dass eine maximale Antwortzeit für eine bestimmte Problemlösung vom System
garantiert wird. Insbesondere sind zeitlich begrenzte Algorithmen
meistens weniger effizient als solche, die nicht einer solchen Beschränkung unterliegen.
Ein Beispiel aus dem Alltag ist das Schlangestehen an einer Kasse eines
beliebigen Geschäftes. Falls die Schlange ständig wächst und wächst, läuft der
KAPITEL 2. GRUNDLAGEN, SYSTEME UND VERFAHREN
8
Prozess des Kassierens nicht in Echtzeit ab. Bleibt hingegen die Länge der
Schlange annähernd konstant, es werden im Schnitt so viele Kunden vorne
an der Kasse verarbeitet“ wie sich hinten anstellen, läuft das Kassieren in
”
Echtzeit.
Nach der Klärung des Begriffs Echtzeitsystem folgt eine für diese Arbeit
ausreichend genaue Definition des Kommunikationsbegriffs, mit dessen Hilfe
der Transport der Ein– und Ausgabe passiert. In [Steu03] fasst der Autor
Systeme und Technologien hinsichtlich Verteilter Echtzeitsysteme (siehe Abschnitt 2.1.2) zusammen. Der Begriff Kommunikation ist dort sehr generell
gehalten: es handelt sich um den Austausch von Materie, Energie oder Information.
Kommunizierende Einheiten in einem System legen gemeinsame Kommunikationsplattformen nahe. Auch wenn die Kommunikation von Materie
mittels spezieller und an den kommunizierten Inhalt angepassten Verbindungen und Pfaden (Rohre für Flüssigkeiten, Transportbänder für solide Stoffe)
realisiert ist, kann Energie und die darin codierte Information über eine gemeinsame Plattform vermittelt werden. Voraussetzung dafür ist die Existenz
standardisierbarer und standardisierter Transportverfahren für Energie und
Information, auf denen entsprechende Transportnetze aufgebaut werden können. Bei der elektrischen Energie sind dies zum Beispiel die Leitungen mit
ihren genormten Spannungsebenen; bei der Information sind es Bussysteme
oder LANs und deren Verbindungen über graphenförmige Netze Transportverfahren.
KAPITEL 2. GRUNDLAGEN, SYSTEME UND VERFAHREN
2.1.2
9
Verteilte Systeme
Es folgt ein Einschub zu Verteilten Systemen. Dabei wird der Begriff der Verteilung intuitiv verwendet, also vorwiegend im Sinne einer räumlichen Verteilung. Zu den Parametern des Raums (z. B. Abmessungen, Dimensionen,
Formen) werden keine weiteren Angaben gemacht. Die so genannte funktionale und logische Verteilung läuft letztendlich in unserer physikalischen
Welt auf eine räumliche Trennung von Funktionen hinaus. Wobei die räumliche Entfernung extrem klein ausfallen (SoC) kann oder gar virtueller Natur
(Prozesse und Threads in Programmiersprachen) ist.
Eine axiomatische Definition beschreibt die strukturellen und physikalischen Grundeigenschaften Verteilter Systeme:
• Die Architektur Verteilter informationsverarbeitender Systeme umfasst
abgrenzbare Einheiten, darunter eine möglicherweise variable Zahl
von Verarbeitungseinheiten, auf denen (Rechen–) Prozesse ablaufen.
• Die Kommunikation zwischen den Prozessen erfolgt durch Nachrichtenaustausch über eine von allen Einheiten bzw. Prozessen gemeinsam genutzte Kommunikationsinfrastruktur.
• Die Kommunikation zwischen den Prozessen unterliegt einer von Null
verschiedenen und variablen Verzögerung.
• In einem Verteilten System ist die Existenz von zwei Prozessen mit
exakt gleicher Sicht auf ihren gegenseitigen Status nicht möglich.
Dasselbe gilt für Sichten auf den Status des Gesamtsystems.
• Es existiert eine systemweite Steuerung für die dynamische (Inter–)
Prozesskommunikation und für das Ablaufmanagement der Prozesse.
Eine Unterteilung solcher Systeme kann anhand der Verteilung des Status
des Gesamtsystems vorgenommen werden: zentrale, verteilte und replizierte
Verwaltung.
2.2
Technische Grundlagen
Zwei großen Kontrahenten beim Design und der Implementierung einer Network Engine sind die Menge der ausgetauschten Daten (data throughput)
und die durch das Netzwerk bedingten Laufzeiten (latency) der versendeten
Nachrichten.
Die Datenmenge die zwischen den teilnehmende Knoten ausgetauscht
werden muss, hängt nicht von der gewählten Topologie des Netzes ab: Das
KAPITEL 2. GRUNDLAGEN, SYSTEME UND VERFAHREN
10
klassische Client-Server Modell bündelt das Gros des Datenaufkommens beim
Server. Je mehr Clients beim Server verwaltet werden, desto größer ist auf
der einen Seite die Menge an Daten, die beim Server eintrifft, als auch auf
der anderen, die die zu den Clients wieder rausgeht.
Dagegen steht die hochgradig vermaschte Topologie des peer-to-peer Netzes, in der jeder Client mit möglichst jedem anderen verbunden ist. Dadurch
entfällt die Datenbündelung beim Server – sie muss jedoch von jedem Client
abgefangen werden. Der Vorteil der vollen Vermaschung liegt in der verkürzten Laufzeit einer Nachricht zwischen den Clients. Hier fehlt die datenvermittelnden Instanz des Servers. Der Nachteil diese Architektur liegt im
steigenden Daten- und Verwaltungsaufwand bei jedem Knoten.
Die Laufzeiten einer Nachricht, auch Verzögerung genannt, hängen direkt mit den Zusicherungen der gewählten Plattform und deren Protokollen
zusammen. Die in der Arbeit gewählte Plattform ist das paketorientierte
Internet Protokoll. Da es sich dabei um einen best effort service handelt,
kann es keine festen Zusicherung an die Laufzeiten eines Paketes (Nachricht)
geben. Im Grunde kann noch nicht einmal die Auslieferung der Pakete zugesichert werden: Es können verschickte Pakete gar nicht, mehrfach oder in
einer anderen Reihenfolge beim Adressaten eintreffen.
2.2.1
Simulationen
Computerspiele sind Simulationen. Simulationen sind Programme, die virtuelle Welten (Spielräume) berechnen und den aktuellen Simulationszustand
ausgeben. Die hier betrachteten Simulationen bestehen aus zwei logisch und
in der Regel auch physikalisch getrennten Einheiten: Server und Client. Der
Server unterhält die Referenzsimulation. Der Server verwaltet ausserdem eine beliebige Anzahl an Clients und dient diesen als Synchronisierungs- und
Nachrichtenaustauschinstanz. Ein Client unterhält eine Kopie der Referenzsimulation und präsentiert diese dem Benutzer.
Benutzeraktionen können den Zustand der Simulation beeinflussen. Dafür
werden die Aktionen von der Clientapplikation interpretiert und in geeigneter
Form (als Nachricht) an die Simulation gesendet. Benutzeraktionen treiben
das Spielgeschehen an und erhalten die Simulation lebendig: die Reaktion
anderer Benutzer auf eine Benutzeraktion äussert sich meist wiederum in
Benutzeraktionen.
Ändert sich der Zustand der Simulation, müssen alle Benutzer über diesen
Umstand möglichst direkt in Kenntnis gesetzt werden. Diese binäre Information reicht theoretisch aus, damit alle Benutzer den neuen Simulationzustand
anfordern.
KAPITEL 2. GRUNDLAGEN, SYSTEME UND VERFAHREN
2.2.2
11
Resourcemanagement
Singhal und Zyda [SiZy99] nennen eine Informationsprinzipgleichung, die die
Menge der benötigen Resourcen einer Netzwerkapplikation im direkten Zusammenhang mit der Nachrichtenanzahl und -größe sowie einem Zeitkriterium angibt:
Resources = M ∗ H ∗ B ∗ T ∗ P
M
H
B
T
P
Anzahl der ausgetauschten Nachrichten
Anzahl der Zielknoten pro Nachricht
Größe pro Nachricht
Rechtzeitigkeitanforderungen
Anzahl der Prozessorzyklen
T = Rechtzeitigkeitanforderungen an die Nachrichtenauslieferung
P = Anzahl der Prozessorzyklen zum Transfer als auch zum Interpretieren pro Nachricht gebraucht werden
Wird der Wert einer beliebigen der fünf Variablen in der Gleichung erniedrigt, so verringert sich der Resourcenbedarf. Diese Einsparung geschieht
aber nicht ohne Kosten. Diese können entweder durch eine Erhöhung einer
der anderer Variablen kompensiert werden oder die Erfahrungsqualität der
Applikation, der Spielspaß, nimmt ab. Die Auswahl der richtigen Variablen
und der Spielraum bei den Werten obliegt primär den vorgegebenen Applikationsanforderungen und den durch die Hard- und Software bedingten
Voraussetzungen.
KAPITEL 2. GRUNDLAGEN, SYSTEME UND VERFAHREN
2.3
12
Bestehende Lösungen
Im folgenden werden bereits verfügbare Verfahren und Systeme vorgestellt.
2.3.1
Java Shared Data Toolkit
Das Java Shared Data Toolkit (JSDT) unterstützt hochgradig interaktive,
kollaborative Java Applikationen. Es bietet eine abstrakte Form einer Session. Damit ist eine Gruppe von Objekten gemeint, die über eine gemeinsame
Kommunikationsschicht miteinander verbunden sind und die sich gegenseitig
beliebige Nachrichten schicken können. Zusätzlich enthält das Toolkit neben
einer effizienten Unterstützung multicast-orientierter Protokolle auch die Fähigkeit sequenzielle Nachrichtenströme einzurichten. Dazu kommt ein tokenbasierter Synchronisationmechanismus und die Möglichkeit eine gemeinsame
Sicht auf Bytearrays zwischen den Teilnehmern einer Session zu gewährleisten.
JSDT stellt lediglich eine einfache Schnittstelle für generelle Mehrparteienkommunikation dar, hinter der eine ganze Reihe von Implementierungen
die eigentliche Arbeit leisten können. Welcher Protokoll Stack zur Laufzeit
aktiv ist, sogar die Auswahl dessen, wird vor dem JSDT-Nutzer versteckt.
JSDT wird standardmäßig mit drei Implementierungen geliefert: Socket
(TCP und UDP), HTTP (inklusive SSL und Tunneling) und LRMP. Letzteres steht für das Light-Weight Reliable Multicast Protocol, welches auf
IP-Multicast aufsetzt.
2.3.2
OpenSkies
Cybernets Real Time Intelliget Routing Technology, genannt OpenSkies, ist
eine verteilte Servertechnik auf der Basis von HLA für den Datentransfer
dynamischer und hoch-interaktiver Netzwerkapplikationen in Echtzeit. Dabei
werden nur solchen Datenströme weitergeleitet, die für einen bestimmten
Client von Interesse sind. Als Grundlage beschreibt Cybernet die Lösung zur
Datenmengenreduktion bei statischen (Web-) Inhalt und bewertet diese für
den Einsatz im dynamischen Fall als nicht passend.
OpenSkies verwendet ein software-basiertes System, das in einem Netzwerk von verteilten Server läuft. Die Clients (hier Federates) verbinden sich
mit einem dieser Server (genannt FedHosts), nachdem der LobbyManager
einen passenden aussuchte. Die FedHosts agieren nun zusammen als Datenverkehrskontrolleure, die die Datenströme in die richtige Richtung leiten. Unter der Vorraussetzung einer ordentlichen Culling-Implementierung, ist jeder
KAPITEL 2. GRUNDLAGEN, SYSTEME UND VERFAHREN
13
Abbildung 2.1: Übersicht eines OpenSkies-basierten Netzwerks
FedHost hauptsächlich mit der Behandlung seiner Federates beschäfigt. Daraus resultiert eine lineare Abhängigkeit zwischen der Anzahl der vom System
unterstützten Clients und Zahl an FedHosts.
Entwickler können eigene Culling-Kriterien implementieren und als
FedHost-Module installieren, deren Strategien sich direkt an der zugrundeliegende Applikation orientieren daraus Vorteile schöpfen können. Beispiele
sind Kriterien wie verschiedene Radiofrequenzen, Distanz zwischen Einheiten, Blickwinkel und eine Aussortierung der Daten nach Rang oder Sicherheitsstufen. Da die Sprache mit der die Culling-Regeln definiert werden C++
ist, können die Regeln beliebig einfach oder komplex sein.
Lineare Skalierung ist laut Cybernet der Schlüssel zur Bandbreitenverwaltung in Netzwerken: Um den Aufwand von mehr Clients aufzufangen, können
entsprechend neue FedHost in das System aufgenommen werden.
KAPITEL 2. GRUNDLAGEN, SYSTEME UND VERFAHREN
2.3.3
14
Terazona
Die Firma Zona, Inc. bietet ein MMPOG Produkt namens Terazona an.
Terazona ist ein software development kit (SDK), das eine Umgebung für
Onlinespiele zur Verfügung stellt, in der pro Cluster bis zu 32.000 Spieler
verwaltet werden können.
Abbildung 2.2: Übersicht der Komponenten von Terazona
Terazona bietet für die Lösung der Skalier- und Sicherheitsprobleme von
MMPOGs eine speziell designte, abstrakte Netzwerkschicht. Spielapplikationen können ohne Rücksicht auf die zugrundeliegende Netzinfrastruktur geschrieben werden. Dieser Ansatz ermöglicht, dass sich die Spieleprogrammierer ausschließlich mit dem Design der Spiele beschäftigen können und sich
nicht um Netzwerkdesign kümmern müssen. Dadurch werden die Entwicklungskosten signifikant gemindert.
Serverseitig verteilt Terazona die anfallende Last auf mehrere Rechner
mit speziellen Aufgabengebieten (n-tier server architecture) wie Applikationsmanagement und Datenbankzugriff. Diese Serverschichten bestehen wiederum aus mehreren eng miteinander gekoppelten Komponenten, bei deren
Implementierung auf Erweiter- und Wiederverwendbarkeit durch den Spieleprogrammierer geachtet wurde. Dazu gehören Module wie eine Validation
Engine, eine Prediction Engine und ein GameState Event Processor.
Plattformunabhängig der Clients, Sicherheit des Gesamtsystems und dessen Fernsteuerbarkeit sind weitere Aspekte, zu denen Terazona eine Lösung
KAPITEL 2. GRUNDLAGEN, SYSTEME UND VERFAHREN
15
anbietet. Clients sind über eine Kommunikationskomponente mit den Servern verbunden, die die eigentliche Komplexität verbirgt und ein Bild eines
einzelnen Servers erzeugt.
Abbildung 2.3: Beispielhafte Installation von Terazona
Eine externe Firewall kontrolliert die Datenströme zwischen den Clients
und dem Zona Dispatcher Server. Der Dispatcher dient als Login Server und
leitet eingeloggte Benutzer an einen geeigneten Game Server weiter. Mit Hilfe
periodischer Heartbeats aktualisiert er auch ständig den Status aller Server.
Die Game Server verarbeiten eintreffende Game States aufgrund der Spiellogik und senden validierte States an alle Clients, die ihnen zugeordnet sind.
Ein Sphere Server überwacht die Aktivitäten der Game Server und stößt im
Absturzfall eines solchen eine Neuzuteilung der betroffenen Clients durch den
Dispatcher an. Zudem kann der Sphere Server auch die Last zwischen den
Game Servern zum Beispiel nach Regionen verteilen. Hier werden auch die
angeschlossen Datenbanken bedient.
KAPITEL 2. GRUNDLAGEN, SYSTEME UND VERFAHREN
2.3.4
16
Sun Game Server Technology
In einem fünfseitigen White Paper [Sun 04] gibt die Firma Sun im Juni 2004
einen Überblick über das in der Entwicklung befindliche Konzept Sun Game
Server Technology. Dieses trägt den Anforderungen hochgradig skalierbarer
Spielsimulationen Rechnung, indem es kurze Antwortzeiten, eine hohe Bandbreite und große Ausfalltoleranz aufweist. Sun ordnet Spiele in die Gruppe
der fast Echtzeitsimulationen ein, die sehr starke Performanzanforderungen
besitzen (siehe Abschnitt 2.1.1). Zwei weitere Punkte werden von den Autoren im Rahmen der MMOG als entscheidend über Erfolg und Misserfolg
eingestuft:
Very Low Latency. Die Gesamtantwortzeit des Systems auf Anfragen jeglicher Art darf wenige Millisekunden nicht überschreiten.
From Hundreds To Tens of Thousands of Players. Das System soll
nicht nur am oberen Ende, der Fall mit 10.000+ Spielern, den Anforderungen gewachsen sein, sondern eben auch bei niedriger Benutzung, nur
einige 100 Spieler sind online, entsprechend weniger Resourcen verbrauchen. Einige Spielformen können im Laufe ihrer Dauer starke Schwankungen der Mitspieleranzahl erfahren. Darauf muss das System stufenlos reagieren können.
Desweiteren nennt Sun den Spieleentwickler als primäre Zielperson ihrer Game Server Technology und grenzt diesen anhand seiner Fähigkeiten gegenüber
dem normalen corporate und enterprise developer ab: Er habe kein Wissen
in den Bereichen Nebenläufigkeit und Datenbanken sowie nur wenig Erfahrung in der Entwicklung skalierender Systeme. Deshalb soll das vorgestellte
Konzept die Einarbeitung des Spieleentwicklers in diese Gebiete abnehmen.
Diese Ziele führen dann auch zu den Grundpfeilern des Sun Game Server
Technology Designs:
Simple Application Model. Eine Applikation besteht aus einer Welt von
serialisierbaren Simulationsobjekten (SO). Um (Simulations-)Aktionen
auszuführen werden die auf ihnen definierten Methoden aufgerufen.
Automated Execution Model. (Die Methoden der) Simulationsobjekte
werden ereignis-gesteuert, race und deadlock-proof in einer ExecutionUmgebung aufgerufen, die auch für die Fehlerbehandlung zuständig ist.
Transparent Continuous Persistence. Die Details der SO-Akquise, das
Beenden einer Ausführungseinheit und das Speichern der Ergebnisse
übernimmt die Execution-Umgebung. Jede Spielaktion wird mit dem
Zeitpunkt ihrer Beendigung dauerhaft.
KAPITEL 2. GRUNDLAGEN, SYSTEME UND VERFAHREN
17
Transparent Massive Scalability. Die Ausführung von Spielaktionen
wird transparent auf ein horizontal skalierbares Feld von unabhägigen
Prozessoren mit getrennten Speichern verteilt. Dabei kann das Feld
einen oder eben sehr viele Prozessoren enthalten.
Die Autoren beschreiben die Game Server Technology mit einem Kernsatz: a single, event-driven, monothreaded programming environment. Die
Programme, innerhalb dieser Umgebung ausgeführt, werden jedoch transparent auf mehrere Prozessoren verteilt, bieten automatische Persistenz und
sind fehlertolerant. Das Konzept sieht eine vertikale Unterteilung in drei
Schichten vor: Communications, Simulation Logic und Object Store.
Die Object Store Schicht enthält alle Zustände aller Spiele die auf dem Game
Server laufen. Es stellt eine hoch effiziente (Bruchteile einer Millisekunde
pro Operation), skalierbare und fehlertolerante Datenbankanbindung dar, die
verschränkungsfreien Zugriff auf die Simulationsobjekte bietet.
Die Simulation Logic Schicht führt die eigentlich Spielalgorithmen aus.
Hier werden aus eingehenden Ereignissen Aufgaben erzeugt, die bei ihrer
Abarbeitung Objekte aus dem Object Store anfordern, verändern und wieder
an die Store-Schicht zurückgeben.
Die Communications Schicht gruppiert die Kommunikation der Spieler.
Sie leitet die Datenpakete sowohl zwischen den Spielern und Simulation Logic
KAPITEL 2. GRUNDLAGEN, SYSTEME UND VERFAHREN
18
Schicht als auch direkt unter den Spielern weiter. Hier wird ebenfalls zwischen
verschiedenen Netzwerkprotokollen, wie HTTP und GPRS, übersetzt.
Die Vorteile dieser Architektur liegen laut Sun in der extremen Skalierbarkeit,
der hohen Fehler- und Ausfalltoleranz, der sehr guten Prozessornutzung und
der totalen Persistenz aller Objekte. Diese Vorzüge werden alle in einem einfachen Programmiermodell vereinigt, das dem Spieleentwickler die Last der
serverseitigen Programmierung nimmt und ihn stattdessen ermöglicht, mehr
Zeit in seine Hauptaufgabe zu investieren: die Entwicklung großartiger Spiele.
Zudem kann die beschrieben Technologie gleich mehrere Spiele nebeneinander ausführen, was den einfachen Einsatz in Serverfarmen ermöglicht und
dessen Administration stark vereinfacht.
2.3.5
Zusammenfassung
Die vorgestellten Systeme bieten alle die Möglichkeit multiplayerfähige Spiele
umzusetzen. Eine Gemeinsamkeit aller betrachteten Systeme ist die Umsetzung eines Client-Server Modells. Dabei zeigen sich sowohl Stärken als auch
Schwachstellen hinsichtlich MMOGs.
Die starre Einteilung der Spielwelt in disjunkte Regionen ist als Lösungsansatz der Lastverteilung zu unflexibel. Weitere Kritikpunkte sind die
begrenzte Skalierbarkeit, eine minimale Fehlertoleranz, ineffiziente Nutzung
vorhandener Prozessoren und eingeschränkte Dauerhaftigkeit der Simulationsdaten.
JSDT definiert zum Beispiel lediglich die öffentliche Schnittstellen für
die Entwicklung kollaborativer Anwendungen. Computerspielprogrammierer
können diese grundlegenden Kommunikationsmethoden für die Übertragung
spielrelevanter Daten nutzen – müssen aber höhere Konzepte wie Spielermanagement und Persistenz der Weltdaten nachträglich einpflegen.
Cybernets Ansatz setzt auf intelligentes Filtern von unerwünschten Datenströmen. Als Filterkriterien werden bei Openskies nicht nur die Zieladressen der Pakete herangezogen, sondern auch deren applikations-spezifischen
Inhalte.
Ingesamt ist die von Sun beschriebene Technologie sehr vielversprechend.
Die Aufgaben der einzelnen Komponenten sind klar definiert und es existieren bereits Speziallösungen, die den gestellte Anforderung der jeweiligen
Gebiete gewachsen sind. Leider fehlen, da sich das Projekt noch in der Forschungsphase befindet, jegliche Eckdaten zur Leistungsfähigkeit im realen
Einsatz.
Im folgenden Kapitel werden die konkreten Anforderung an eine MMOG
Engine gestellt.
Kapitel 3
Anforderungsanalyse
Die Betrachtung der Computerspielegeschichte und die Analyse bestehender
Systeme aus dem vorherigen Kapitel zeigen, dass während der Entwicklung
einer MMOG Engine eine Reihe von interessanten Herausforderungen gemeistert werden müssen. Zunächst ist hier die Erstellung und Verwaltung großer
und veränderbarer Welten zu nennen. Zudem sind die Spieler dieser simulierten Welten online – Aktionen eines Spielers können den Spielzustand ändern
und müssen den Mitspielern mitgeteilt werden. Die Welt und die Spielerdaten
sollen offline-Phasen überdauern und müssen damit speicherbar sein. Eine
der größten Herausforderungen ist die hohe Anzahl gleichtzeitig agierender
Spieler, welche eine sehr effiziente Nutzung der Netzwerkresourcen erfordert.
Aus diesen Rahmenbedingungen ergeben sich Anforderungen sowohl an
das Gesamtsystem als auch an Client und Server, die im Weiteren dargestellt
werden.
3.1
Anforderungen an das System
Es folgen allgemeine Zielsetzungen an die Beschaffenheit des Gesamtsystems,
die im Design und in der Implementierung umzusetzen sind.
Echtzeit Die Spieler interagieren mit der Welt nicht rundenbasiert oder in
starren Zeitfenstern. Jeder Spieler hat zu jeder Zeit die Möglichkeit die
im Kontext zur Verfügung stehenden Aktion auszuführen. Der Spieler
sieht immer den aktuellen Stand der Welt und kann unmittelbar agieren
und reagieren: fast paced action – that’s the game.
Gleichzeitigkeit Es sollen gleichzeitig hunderte Spieler in ein und derselben
Welt interagieren.
19
KAPITEL 3. ANFORDERUNGSANALYSE
20
Abstraktion Die API soll von der Implementierung getrennt sein. Der Nutzen dieser Vorgehensweise liegt zum einen in der selbst–beschreibenden
Eigenschaft API. Dort sind alle Komponenten, deren Aufgaben, Rollen
und Fähigkeiten deklarativ festgehalten. Zum anderen ist die Implementierung komponentenweise oder auch komplett austauschbar.
Referenzimplementierung Das Basissystem soll die API beispielhaft implementieren und dadurch die Machbarkeit belegen. Optimierungen
spielen dabei eine untergeordnete Rolle.
Klarheit Die Schnittstellen, die die Anwendungsprogrammierer ansprechen,
sollen klar nach Themen– und Aufgabengebieten strukturiert sein. Die
Namensgebung soll selbsterklärend und intuitiv verständlich sein.
Skalierbarkeit Das System soll sich, gestützt durch entsprechende Hardware, an höhere Anforderungen und Belastungen anpassen können. Diese
Anpassung sollte im Idealfall zur Laufzeit geschehen, ohne die dabei
Simulation abzubrechen und neuzustarten.
Erweiterbarkeit Anwendungsprogrammierer sollen eigene Welten erschaffen können. Die dazu benötigten Erweiterungen sollen sich problemlos
in das bestehende System an dafür vorgesehene Stellen einfügen lassen.
Robustheit Da Netzwerke, die auf dem Internet Protokoll basieren, sehr
dynamisch hinsichtlich ihrer Zusammensetzung sind muss sich das System den Änderungen anpassen können. Der Ausfall eines Knotens soll
das System in seiner Funktion nicht beeinträchtigen. Weiterhin sichert
das Internet Protokoll selbst lediglich eine best effort Dienstqualität zu.
Die damit verbunden Unsicherheiten (beispielsweise Paketverlust, Paketverdopplung oder Paketunordnung) müssen vom System erwartet,
behandelt und, soweit möglich, behoben werden.
Laufzeitstatistik Es sollen bereits zur Laufzeit wichtige Leistungsdaten ermittelt und ständig aktualisiert werden. Mithilfe dieser Größen soll
das System dynamisch an die aktuelle Belastungen angepasst werden.
Wichtige Kennzahlen im technischen Kontext zum Beispiel die Anzahl
aktiver Spieler und die Bandbreite, die jeder Spieler verwendet.
3.2
Anforderung an den Client
Ein Client besteht aus vielen Komponenten, die ihre jeweilige Aufgabe erfüllen. Zu diesen Komponenten gehören unter anderem die graphische Benut-
KAPITEL 3. ANFORDERUNGSANALYSE
21
zerschnittstelle, das Audiosystem, die lokale Simulation und auch das Abfragen der Benutzereingaben. Die globale Simulation beeinflussenden Eingaben
müssen über die Netzwerkkomponente, hier der Einfachheit halber Client
genannt, an den Server geschickt werden. Im Folgenden sollen die Anforderungen an eben diese Netzwerkkomponente vorgestellt werden.
Einfache Integration Der Client soll lediglich eine weitere Komponente
im Objektbaukasten des Spieleprogrammierers sein. Als solche hat sie
sich an die Standards der Namensgebung und der vom Programmierer
erwarteten Verhaltens- und Funktionsweisen zu halten.
Nebenläufigkeit Der Client soll die Applikation nicht in ihrem eigentlichen
Fluss unterbrechen. Das bedeutet zum Beispiel, dass das Warten auf
Daten vom Server nicht das Rendern der Welt unterbrechen darf. Dem
Spieleprogrammierer soll dennoch eine Möglichkeit an die Hand gegeben werden, mit der er bewusst den Fluss der Applikation an Ereignisse
des Clients koppeln kann.
Synchronisierung Da der Client auf die gleichen Datenfelder zugreift wie
die anderen Komponenten, muss der Zugriff entweder zeitlich getrennt
oder durch ein geeignetes Verfahren (Kopieren beim Lesen) gegeneinander entkoppelt werden.
3.3
Anforderungen an den Server
Ebenso wie der Client muss der Server neben den allgemeinen Anforderungen
an das Gesamtsystem spezielle Bedingungen erfüllen.
Echtzeit Änderungen in der Simulation, die entweder durch Benutzeraktionen oder durch das Fortschreiten der Zeit eintreten, sollen in möglichst
kurzer Zeit an den Clients vermittelt werden. Diese Anforderung an
den Server hat einen großen Einfluss auf die Spielbarkeit und damit
den Spielspaß eines Onlinespiels.
Skalierbarkeit Mehrerer Prozessoren sollen (durch Threads) und/oder
Lastverteilung auf vertrauenswürdige, meist eng in einem LAN gekoppelte Rechner unterstützt werden.
Persistenz Die für die Simulation relevanten Daten, meist in Objekten gekapselt, sollen einen Neustart des Servers automatisch überstehen ohne
dass der Spieleprogrammierer dafür extra einen Speichermechanismus
KAPITEL 3. ANFORDERUNGSANALYSE
22
anstoßen muss. In diese Kategorie fallen zum Beispiel die Charakterdaten wie Namen, Fähigkeits- und Erfahrungspunkte sowie die im Besitz
des Spielers befindlichen Gegenstände.
Sicherheit Fehlerhafte Nachrichten, falscher Inhalt oder zeitliche Ungereimtheiten, sollen erkannt werden können. Als Reaktion stehen mehrere Stufen des Ausschlusses zur Verfügung:
• Der Spieler kann unverzüglich vom Spiel ausgeschlossen werden(kick).
• Der Zugang zum Spiel kann für bestimmten Internetadressen mit
zeitlicher Begrenzung oder bis auf Widerruf durch einen Administrator verweigert werden (IP ban).
• Wenn das Spiel einen eindeutigen Schlüssel für jeden Client vorsieht, kann auf dieser Ebene eine Sperrung eingerichtet werden
(key ban).
Kontrolle Der Server soll zur Laufzeit ohne Spielbeitritt eines Administrators konfigurierbar sein. Darunter fällt neben der transparenten Sicht
in das laufende System die Möglichkeit dort Veränderungen zu veranlassen.
Kapitel 4
Design
Im folgenden Kapitel wird das Design einer Massive Multiplayer Network Engine vorgestellt, die die Anforderungen der vorangegangenen nalyse umsetzt.
Sie erhält das Akronym Mamuneen.
4.1
Mamuneen
Mamuneen ist eine an die Anforderungen von MMOGs serverseitig angepasste Variante des klassischen Client-Server Konzepts.
Server
Client
Client
Client
I
N
T
E
R
N
E
T
Proxy
Transceiver
Server
Proxy
Database
Server
Proxy
Server
Abbildung 4.1: Die fünf Komponenten von Mamuneen sind Client, Proxy,
Transceiver, Server und Database
Der Server ist in die drei Schichten Transceiver, Server und Database aufgeteilt. Die Transceiver verwalten für jeden Client eine lokales Abbild (Proxy),
über das die Daten von und zu den Clients vermittelt werden. Jeder Server
23
KAPITEL 4. DESIGN
24
enthält das gesamte Regelwerk und kann die Simulation vorantreiben. Die
Database dient der gemeinsamen Datenspeicherung aller Server.
Mamuneen folgt im Aufbau strikt dem Model-View-Controller (MVC)
Designpattern [GHJV95]. Die logisch eng miteinander gekoppelten Einheiten Modell (M) und Steuerung (C) werden hier von der Datenbank und den
Servern übernommen. Letztere vereinigen die Simulationlogik und bilden somit das Regelwerk des Spiels ab. Die Server stellen die Schiedsrichterinstanz
gegenüber den Clients (entferntes V) dar. Das Modell beinhaltet die Daten
in Reinform und stellt zunächst bequeme und schnelle Zugriffsmethoden auf
einzelne Datenelemente zur Verfügung. Die Zugriffsmethoden werden von der
Steuerungseinheit verwendet um Änderungen im Modell vorzunehmen. Eine
weitere Aufgabe liegt in der Verwaltung von Ansichten (V), die, sobald sich
das Modell ändert, von diesem über die Art der Änderung benachrichtigt
werden. Diese Ansichten sind ihrerseits die Steuerungseinheiten der Clients
und werden im vorliegenden Design als Proxys bezeichnet.
Die Clients nehmen demnach lediglich die Rollen entferntes View und
eingeschränkter Controller ein. Entfernt bedeutet in diesem Zusammenhang,
dass die Clients auf einem anderen Rechner als die Serverkomponenten laufen. Eingeschränkt beschreibt, dass ein Server (genauer das implementierte
Regelwerk) die vom Client initiierten Modelländerungen anpassen, abändern
oder sogar ablehnen kann. Dieses Vetorecht des Servers ist insbesondere hinsichtlich unfairer Spieler (Cheater) von Nöten. Zusätzlich bietet die zentrale
Datenverwaltung auch eine einfache Möglichkeit der Abrechnung, falls die
angebotenen Leistungen oder Spielzeiten kostenpflichtig sein sollen.
4.2
Drop’n Pick!
B
A
C
Abbildung 4.2: Drop’n Pick!
Eine detailierte Beschreibung einer typischen
Spielsituation mit drei Spielern soll einen anschaulichen Einstieg in die Funktionsweise der
in Abschnitt 4.1 skizzierten Komponenten von
Mamuneen bieten. Der Fokus liegt auf der
Analyse des Flusses von Informationseinheiten
in einer Mehrspielersimulation. Bereits in diesem Fall lassen sich die Phänomene der unweigerlich eintretenden Inkonsistenz verteilter
Anwendungen beobachten. Diese Beobachtung
sind Grundlagen der später festgehaltenen Aufgaben und Pflichten der Serverkomponente.
KAPITEL 4. DESIGN
25
Das betrachtete Spiel ist sehr simpel aufgebaut. Die drei Spieler bewegen sich frei auf
einer Ebene und können auf dem Boden befindliche Gegenstände aufheben
und bereits aufgenommen Gegenstände wieder ablegen. Spieler A wird zunächst einen Gegenstand ablegen. Die beiden Kontrahenten versuchen dann
diesen aufzuheben.
4.2.1
DropItemCommand
Die Clientapplikation interpretiert eine Aktion des Benutzers A (Tastaturdruck, Maustastenklick oder ähnliches) als dessen Wunsch einen Gegenstand
auf der Erde abzulegen.
Dazu generiert die Applikation das entsprechende Kommando new DropItemCommand(34527), mit dem genau zwei Fakten
festgehalten werden. Erstens wird die gewünschte Aktion eindeutig durch den Namen des Kommandos festgelegt und zweiClient
tens bestimmt der (hier zufällig gewählte) Zahlparameter das
Objekt der Handlung. Das Kommando wird zu einem Datenpaket konvertiert und an den Server versendet. Dabei nutzt das
Paket den bereits etablierten Kanal zu seinem Proxy.
Serverseitig wird jedes Paket von genau einem Transceiver entgegenommen. Dort angekommen, kann das Paket
anhand seines Proxys genau dem Benutzer A zugeordnet
werden. Damit ist die Position, an der das System gleich
Transceiver
einen Gegenstand generieren wird, eindeutig bestimmbar.
Doch bis dahin ist es noch ein langer Weg. Zunächst bleibt
das Datenpaket intakt – und sein Inhalt unangetastet.
Nachdem ein Paket vollständig empfangen wurde, wird
es zusammen mit einem Eingangsstempel in eine Arbeitskapsel gesteckt. Ein Eingangsstempel gibt Auskunft über
den Absender und die Empfangszeit der Kapsel. Alle Arbeitskapseln werden
in der Reihenfolge ihrer Erstellung am Ende einer Warteschlange angefügt.
Am Kopf dieser Schlange stehen dann die eigentlichen Arbeiter“: die Server.
”
Die Server enthalten den Code der die Simulation beschreibt. Ein Server nimmt sich immer genau einer Kapsel
an. Sollten alle Server beschäftigt sein – es steht also kein
Server
freier Arbeiter“ am Kopfende der Schlange – und gleich”
zeitig neue Arbeitskapsel entstehen, wächst diese bis zu einer kritischen Länge. Dem Transceiver steht zur Pufferung
der Kapseln zu wenig Arbeitsspeicher zur Verfügung. Spätestens jetzt müssen Gegenmaßnahmen eingeleitet werden.
KAPITEL 4. DESIGN
26
Es bieten sich zwei Möglichkeiten an um die Schlange zu
verkürzen.
Defensiv durch den Ausschluss von Spielern: Die damit einhergehende Reduktion der kapselgenerierenden Clients verringert die Produktionsrate neuer Aufträge. Weiterhin man kann alle Kapseln der ausgeschlossenen Clients aus der Schlange herausnehmen. Dieses Vorgehen
zieht aber unweigerlich den Unmut der hinter den Clients sitzenden
Benutzer auf die Serverbetreiber.
Offensiv durch das Engagement neuer Arbeiter“: Es werden neue
”
Arbeiter“ engagiert, bis die Warteschlange schrumpft. Wenn auf der
”
Gegenseite die Kapselproduktion herunterfährt, entlässt ein geeigneter
Mechanismus gerade so viele Server, bis sich Produktion und Konsum
die Waage halten. Das ist das optimale Verhalten eines im Abschnitt
2.1.1 beschriebenen Echtzeitsystems.
Nach diesem Exkurs zum Producer/Consumer–Pattern
geht es nun zurück zum eigentlichen Verarbeitungsprozess. Hat ein Server eine Datenkapsel zugeteilt bekommen,
macht er sich schnellstmöglich an deren Verarbeitung. Da- Server
zu wandelt er das gekapselte Datenpacket in das entsprechende Kommando-Objekt um. Das resultierende Objekt
wird als Parameter der Methode execute(DataCapsule,
Database
DropItemCommand) übergeben. Hier kann nun endlich die
Spiellogik greifen und die gewünschte Aktion ausführen.
In diesem Beispiel fordert der Server von der Datenbank
zwei Referenzen zur Änderung an: das Spielerobjekt des Benutzers A und die
Liste aller Bodenobjekte. Die Nummer des Objekts, das fallengelassen werden
soll, wird aus dem Kommando extrahiert.
Als nächstes wird die Nummer (und somit der Gegenstand) aus dem Inventar des Spielers gelöscht und dann
der Bodenobjektliste hinzugefügt. Dabei erhält der Gegenstand eine Position in der Nähe des Spielers A. Beide
Referenzen werden wieder an die Datenbank zurückgegeben und damit wird die Transaktion abgeschlossen. Zu
diesem Zeitpunkt ist die Veränderung in der Simulationsdatenbank bereits dauerhaft und wird sich in der nächsten
Anforderung der veränderten Objekte widerspiegeln. Diese Beobachtung wird im Abschnitt 4.2.2, der das Aufheben
dieses Gegenstandes beschreibt, eine große Rolle spielen.
A
KAPITEL 4. DESIGN
27
Zum Abschluss fehlt noch die Benachrichtung an alle Benutzer, dass ein neuer Gegenstand auf dem Boden liegt. Dieses Ereignis wird durch das Kommando new NewItemCommand(34527, COIN, x, y) beschrieben. Neben der
Identifikationsnummer ist der Typ und der Ort des Gegenstandes codiert.
Die Information, wer den Gegenstand fallen ließ, geht in diesem Fall verloren. Der Vorteil dieses generischen Kommandos ist, dass es dadurch auch
dazu verwendet werden kann, neue, system–generierte Gegenstände in das
Spiel einzuführen.
Das NewItemCommand–Kommando wird zu einem Datenpaket konvertiert und in einer Ausgangskapsel eingebettet, die Auskunft über die Zielgruppe gibt. Die Datenkapsel wird an eine Multicastgruppe gesendet, in der jeder
Server
Transceiver als Konsument registriert ist. Empfängt ein
Transceiver eine auswärts gerichtete Kapsel über die Multicastgruppe, entpackt er diese und fügt jedem Proxy eine
Kopie des gekapselten Datenpakets hinzu. Das Kopieren
ist notwendig, da jeder Proxy die Daten in einem anderen
Tempo an seinen Client sendet.
Damit ist die serverseitige Bearbeitung des Commands abgeschlossen – das
NewItemCommand-Datenpaket trifft bei den Receivern der Clientapplikationen
ein. Da es hier jeweils nur einen Kanal gibt, aus dem Daten (vom Server)
kommen können, reicht eine einzige aktive Instanz zur Bearbeitung eingetroffener Pakete. Der Algorithmus, der dem empfangenen Kommando ein
Verarbeitungsmethode zuordnet, gestaltet sich analog zu dem Algorithmus
der beim Server beschriebenen Verfahren.
4.2.2
PickItemQuery
Im weiteren Spielverlauf sehen alle Benutzer den neuen Gegenstand (fast)
gleichzeitig auf ihrem Bildschirm auftauchen. Selbst dieser spezielle und sehr
unwahrscheinliche Fall kann den weiteren Ablauf des Spiels nicht konistenter
machen: Spieler B und C wollen beide den neuen Gegenstand aufnehmen und
senden diesen Wunsch gleichzeitig ab. Sollten die zwei Kommandos, weiterhin parallel, auch noch in verschiedenen Servern abgearbeitet werden, muss
spätestens hier eine Synchronisierung stattfinden. Diese Aufgabe übernimmt
die Datenbank. Die nächsten Absätze beschreiben den Vorgang detailierter.
Succeeded Spieler B sieht in der Nähe von Spieler A einen neuen Gegenstand auf dem Boden erscheinen. B verkürzt die Distanz und erhält durch
das Benutzerinterface eine Möglichkeit angeboten, den Gegenstand aufzuheben. B gibt den entsprechenden Befehl, woraufhin die Clientapplikation
KAPITEL 4. DESIGN
28
das new PickItemQuery(34527) generiert. Das Kommando wird konvertiert, verpackt und versendet. Bei einem Server angekommen, nimmt es seinen Weg bis hin zu der PickItemReply execute(PickItemQuery) Methode.
Wie beim Fallenlassen werden zwei Referenzen von der Datenbank angefordert: das Spielerobjekt des Benutzers B und die Liste aller Bodenobjekte. Da
zu dem Zeitpunkt der Anforderung keine weiteren Zugriffe auf das Spielerobjekt oder die Liste der Bodenobjekte stattfinden, erhält der aktive Server
prompt die benötigten Referenzen. Die Transaktion ist abgeschlossen, sobald
der Gegenstand der Bodenobjektliste entnommen und der Inventarliste von
B hinzugefügt wurde. Von diesem Zeitpunkt an können wieder andere Server
auf die Objekte in der Datenbank zugreifen. Danach werden zwei Kommandos erzeugt:
1. Dem Spieler B muss signalisiert werden, dass er den neuen Gegenstand
erfolgreich aufnahm. Damit kann die Clientapplikation des Spieler B
ihrerseits zwei Änderung an der visuellen Präsentation der Welt vornehmen: das Entfernen des Gegenstandes vom Boden und die Aktualisierung der Inventarliste. Damit gleicht die von B gesehene Welt –
zumindest in Bezug auf den Gegenstand 34527 – wieder der im Server
vorliegenden Version.
2. Jedem anderen Spieler muss zur konsistenten Darstellung der Welt mitgeteilt werden, dass der Gegenstand nicht länger auf dem Boden liegt.
Dazu dient das Kommando RemoveItemCommand(34527). Sobald Client A und C dieses Kommando ausführen, ist ihre Ansicht wieder auf
dem aktuellen Stand der Dinge. Allerdings ist bis zur Ausführung des
Kommandos der Gegenstand auf dem Bildschirm präsent und es ist für
A und C legitim diesen aufheben zu wollen.
Somit kann es während der gesamten Zeitspanne zwischen den Kommandos NewItemCommand und RemoveItemCommand zu Konflikten kommen. Eine
mögliche Konfliktbehandlung wird im nächsten Abschnitt erläutert.
Failed Auch Spieler C möchte den Gegenstand, der gerade in der Nähe von
Spieler A auf dem Boden erschien, aufnehmen. Das von diesem Client generierte Kommando new PickItemQuery(34527) erreicht einen anderen Server
als das von Spieler B gesendete Pendant. Die zur gleichen Zeit ausgeführte
Behandlungsmethode fordert von der Datenbank neben des Spielerobjekts
C ebenfalls die Bodenobjektliste zum Verändern an. In diesem Fall ist die
parallele von Spieler B angestoßene Transaktion noch nicht beendet. Dieser
Zugriffskonflikt führt zu einer Verzögerung der Datenbankanfrage. Sobald die
KAPITEL 4. DESIGN
29
Datenbank die Referenz auf die Liste der Bodenobjekte an diesen Server gibt,
kann die Verarbeitung fortgesetzt werden. Nun wird bei dem Versuch, das gewünschte Objekt aus der Bodenobjektliste zu entfernen, festgestellt, dass sich
kein Objekt mit dieser Nummer in der Liste befindet. Dieser Fehlstand wird
von der Methode mit der Generation eines PickItemReply$Failed Kommandos als Rückgabewert der Methode quittiert.
Der Transceiver übermittelt das Kommando so schnell wie möglich an
die Clientapplikation des Spielers C. Dort angekommen, kann das Benutzerinterface den misslungenen Versuch, den Gegenstand 34527 aufzunehmen,
geeignet darstellen (akustische oder optische Fehlermeldung). Desweiteren
kann auf Grund dieser Information versucht werden den Gegenstand aus
dem Datenmodell des Clients zu entfernen.
4.2.3
Zusammenfassung
Die detailierte Beobachtung einer Mehrspielersituaton konnte neben der Identifikation einzelner Komponenten und deren Aufgaben auch einige inhärente
Konsistenzprobleme und mögliche Lösungsansätze darstellen.
• Die dabei willkürlich gewählte Beschränkung auf drei Spieler ist kein
Verstoß gegen die Allgemeingültigkeit der gemachten Beobachtung.
• Das dem Datentransfer zugrundeliegende Netzwerkprotokoll sollte verbindungsorientiert sein und die Übertragung der Kommandos zusichern.
• Auf Client– und Serverseite werden Kommandos verpackt, versendet,
empfangen und ausgeführt. Daher liegt es nahe, dafür eine gemeinsame
Basisklasse vorzusehen.
Bevor die Komponenten sowie ihre Aufgaben und Zuständigkeiten dokumentiert werden, wird eine Lösungsmöglichkeit für den Beitritt neuer Spieler
in ein laufendes Spiel beschrieben.
KAPITEL 4. DESIGN
4.3
30
Late-Join
Im Allgemeinen versteht man unter einem Late-Join Vorgang die Integration
einer neuen Komponente in ein bereits bestehendes System. Die Schwierigkeit
des Vorgangs wird durch die Aktivitäten der Systemkomponenten bedingt, da
sich bei hoher Aktivität der Systemzustand laufend ändert. Die Integration
gestaltet sich bei Systemen mit wenig und selten miteinander kommunizierenden Komponenten einfacher als bei solchen, die viel und ständig Informationen austauschen. In einem ereignisgesteuerten System wie Mamuneen
reicht es daher aus, die Ereignisbehandlung der von der Integration betroffenen Komponenten für der Änderungszeitraum zu unterbinden. Dadurch
kann sich der Zustand der Komponenten nicht ändern; sie sind eingefro”
ren“. Die neue Komponente kann nun in die fixierte Umgebung integriert
werden. Nachdem dieser Vorgang möglichst zügig abgeschlossen ist, wird die
Ereignisbehandlung für alle Komponenten, inklusive der neuen, aktiviert.
Das Late-Join Problem lässt sich in dieser Arbeit an zwei Stellen erkennen:
bei dem Beitritt eines neuen Spielers in die bereits belebte virtuelle Welt und
bei der Zuschaltung neuer Serverkomponenten, um der anfallenden Mehrbelastung durch viele neue Clients Rechnung zu tragen. Im Folgenden wird der
konkrete Ablauf eines Late-Join Vorgangs eines neuen Spielers beschrieben.
4.3.1
Late-Join eines Clients
Der Server muss der Clientapplikation des neuen Spielers, im Folgenden Joiner genannt, den aktuellen Spielstand übermitteln. Der gesamte Spielstand
setzt sich aus den Zuständen der einzelnen Spielobjekte zusammen. Diese
Objekte kapseln entweder sehr primitive (ein Zahlwert, eine Zeichenkette)
oder beliebig komplexe Datenstrukturen (Listen oder Bäume von Objekten).
In der hier vorgestellten Lösung muss für jedes dieser Spielobjekte ein Query/Reply - Kommandopaar vom Spielprogrammierer implementiert werden.
Mit dem Querykommando spezifiziert der Joiner das Spielobjekt eindeutig
und fordert vom Server dessen Zustand an. Der Server packt in das Replykommando alle benötigten Daten, aus denen der Joiner das Spielobjekt
vollständig rekonstruieren kann. Zur Zeit des Packens der Spielobjektdaten
darf keine Veränderung an diesen vorgenommen werden. Das Replykommando stellt demnach eine Momentaufnahme des kompletten Objekts zu einem
bestimmten Zeitpunkt dar. Sobald das Auslesen der Daten beendet ist, wird
es auf dem normalen Weg über das Proxyobjekt zum Joiner übermittelt.
Danach können andere Kommandos die Daten wieder ändern. Je nach Datenmenge kann die Übermittlung des Replykommandos etwas länger dauern.
KAPITEL 4. DESIGN
31
Alle weiteren Veränderungen, die das Spielobjekt in der Zwischenzeit erfährt,
werden an die Warteschlange des Proxys angefügt.
Beispiel Anhand der Liste aller auf dem Boden befindlichen Gegenstände des im vorherigen Abschnitt beschriebenen Spiels Drop’n Pick wird ein
Late-Join Vorgang ausgeführt. Zunächst sendet der Joiner mittels eines AllItemsQuery-Kommandos die Anfrage an den Server und wartet auf den
Empfang der entsprechenden AllItemsReply-Instanz. Der Server packt die
Daten aller Gegenstände in das Reply-Kommando und stellt sicher, dass in
dieser Zeit kein DropItemCommand oder PickItemQuery die Liste verändern
kann. Das je nach Größe mit Java-Boardmitteln komprimierte AllItemsReply-Paket wird an den Proxy des Joiners übergeben und von diesem schnellst
möglich übertragen. Damit ist sichergestellt, dass alle nun folgenden NewItemCommand und RemoveItemCommands auf der aktuellen Version der Liste
arbeiten.
Die Liste aller Spieler und deren jeweilige Daten wie ID und Name stellt
Mamuneen dem Joiner automatisch bereit.
4.4
Komponenten
Die abgebildeten Klassendiagramme veranschaulichen die Mitglieder der Typen und geben erste Hinweise auf ihre Anwendung und Aufgaben im Gesamtkonzept. Wichtige Eigenschaften sind genauer erläutert, andere bewusst, da
selbsterklärend, kommentarlos belassen.
4.4.1
Command
Die abstrakte Klasse Command ist als FlyWeight [GHJV95, Mets02] modelliert, da zur Laufzeit sehr viele Instanzen dieser Klasse erzeugt werden und
mehrere Komponenten diese Instanzen referenzieren. Die Spieleentwickler
müssen ihre Kommandoklassen von dieser Basisklasse ableiten. Dadurch wird
sichergestellt, dass jedes Kommando sich selbständig aus einem Datenpuffer
wiederherstellen und auch umgekehrt seine Daten in einen Puffer schreiben
kann. Dazu dienen die folgenden Methoden:
void dataIn(ByteBuffer in) throws BufferUnderflowException;
void dataOut(ByteBuffer out) throws BufferOverflowException;
Die in der Signatur verwendete Klasse ByteBuffer und die beiden Exceptions
sind im Paket java.nio enthalten. ByteBuffer-Objekte beschleunigen den
nativen Zugriff auf die in ihnen verwalteten Daten durch das Betriebssystem.
KAPITEL 4. DESIGN
32
Zudem sollen ableitende Klassen die Änderungen, die dem Modell bei der
Ausführung eines solchen Commands widerfahren, möglichst in ihrem Namen
genau beschreiben:
class RemoveArbitraryItemFromFloor extends Command {
...
}
Der voll-qualifizierte Klassenname dient zur eindeutigen Identifikation der
Command-Klasse. Dadurch können spezielle Beschleunigungstrukturen (wie
indizierte Listen) zum Einsatz kommen, die auch gleichzeitig die Datengröße
eines serialisierten Kommandoobjekts minimieren.
4.4.2
Application
Das Interface Application repräsentiert ein durch Command-Objekte (siehe
Abschnitt 4.4.1) gesteuertes und angetriebenes Objekt. Es bildet die Grundlage der exekutiven Komponente von Mamuneen und ist die Basisklasse der
nachfolgend beschriebenen Client- und Serverklassen.
«interface»
mamuneen::Application
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
addCommandClass(Class) : void
addContext(Context) : void
addExecutor(Executor) : void
getName() : String
getPhase() : Phase
getRunningMillis() : long
getState() : State
isRunning() : boolean
isStartable() : boolean
isStopped() : boolean
isVerbose() : boolean
process(Command) : void
setName(String) : void
setVerbose(boolean) : void
start() : void
stop() : void
Der Lebenszyklus einer Application gestaltet sich dreistufig:
• Erzeugung (inklusive Vorinitialisierung)
• Initialisierung (optional, aber empfohlen)
• Start/Stopp (wechselseitig)
KAPITEL 4. DESIGN
33
Erzeugung
Durch die Erzeugung eines Application-Objekts erhält der Anwender eine
Referenz auf dieses zurück. Die Eigenschaften des Objekts sollen nach der
Erzeugung sinnvoll vorbelegt (vorinitialsiert) sein und ein Starten ohne weitere Initialisierung ermöglichen.
Initialisierung
Die Initialisierung eines Application-Objekts erfolgt nach dessen Erzeugung.
Eine bestimmte Reihenfolgen der Initialisierungsschritte ist implementationsabhängig, sollte jedoch möglichst willkürlich geschehen dürfen, damit eine
hohe Flexibilität bezüglich der Vorlieben des Anwenders gewährleistet ist.
Die wichtigsten Initialisierungsmethoden sind:
setName(String name) Setzt den Namen der Applikation auf name. Der
Name einer Applikation soll nicht nur in der toString() Methode anwendung finden, sondern insbesondere dem Endanwender visuell präsentiert werden.
addCommandClass(Class commandClass) Registriert die angebene Klasse
commandClass in der Liste der der Application bekannten CommandKlassen. Die Behandlung von Duplikaten ist der Implementierung überlassen.
addContext(Context context) Registriert das bereits instantiierte
Context-Objekt context. Der erste Aufruf dieser Methode soll den
sogenannte Root-Context registrieren.
Zusicherungen an die Konstrukturen werden hier nicht gemacht. Es sollten jedoch sinnvolle Konstruktoren dem bequemen Anwender an die Hand gegeben werden, die das erzeugte Application-Objekt mittels der eben beschriebenen Methoden entsprechend initialisieren. Insbesondere der leere Konstruktor soll das erzeugte Objekt in einem startbaren Zustand abliefern.
Start/Stopp
Die start()-Methode startet ein bereits initialisiertes Application-Objekt.
Dabei ist der Implementierung überlassen, wie sie auf nicht ausreichend initialsierte Felder reagiert oder ob sie auch das Setzen neuer Werte zur Laufzeit
zulässt.
Eine Nachbedingung der start()-Methode ist, dass der aufrufende
Thread1 zurückkehrt und nicht in einer Endlosschleife, insbesondere nicht in
1
Thread of control, Kontrollfluss
KAPITEL 4. DESIGN
34
der Command-Execution-Loop, verharrt. Dieses Verhalten gleicht dem der
Methoden Thread#start() und JComponent#show().
Nach ihrer Beendigung soll start() das Application-Objekt in einem
aktivem Zustand überlassen, indem es angelieferte Command-Objekte verarbeiten kann. Unbekannte Command-Objekte, also Instanzen von vorher
nicht via Application#addCommandClass(Class) der Applikation mitgeteilten Klassen, sind zu ignorieren.
Die stop()-Methode hält die Applikation an und setzt sie optional in
den Zustand vor dem Start um einen schnellen Neustart ohne Neuerzeugung
des Application-Objekts zu ermöglichen. Diese Methode soll erst dann zum
Aufrufer zurückkehren, wenn alle Threads unterbrochen und beendet sind.
Kapitel 5
Implementierung
Nachdem aus der Anforderungsanalyse das Mamuneen Konzept entwickelt
wurde, wird in diesem Kapitel dessen Umsetzung in Auszügen beschrieben.
5.1
Externe Bibliotheken
Neben den Standardpaketen des Java Development Kits wurden Bibliotheken
dritter Anbieter eingesetzt. Die im Rahmen dieser Diplomarbeit wichtigsten
sollen im Folgenden erläutert werden.
5.1.1
Logging
Loggen ist ein gängiges Mittel den Ablauf eines Programmes nachvollziehbar zu machen. Für die Erstellung der Log-Nachrichten wird sowohl beim
Server als auch beim Client Jakarta Commons Logging (JCL) von Apache
verwendet:
The Logging package is an ultra-thin bridge between different logging libraries. Commons components may use the Logging API to
remove compile-time and run-time dependencies on any particular
logging package, and contributors may write Log implementations
for the library of their choice.
Die Referenzimplementierung nutzt das Paket Apache Log4j, weil es sehr
effizient programmiert ist und sich das Verhalten ohne Neukompilieren des
Spiels anpassen lässt.
35
KAPITEL 5. IMPLEMENTIERUNG
5.1.2
36
Webadmin
Eine Anforderung an den Server ist die Übersicht und Kontrolle über das
aktuelle Spielgeschehen. Die in dieser Arbeit implementierte Lösung bietet
diese Kontrolle ohne dass der Administrator in das Spiel einsteigen muss.
Jetty ist der Name für ein komplett in Java geschriebenen Webserver
und Servletcontainer von Mortbay. Jetty bietet neben dem eigenständigen
Betriebsmodus auch die Möglichkeit innerhalb eines anderen Programmes
eingebettet zu werden. Durch die Intergration in einen Mamuneen Server
stehen dem Webserver alle Daten des Spiels zur Verfügung. Diese Daten
können bei der Generation der Webseiten einfach aus den Java-Objekten
ausgelesen werden. Damit wird dem Administrator ein unmittelbarer Einblick
in den Spielzustand gewährt.
Der direkte Zugriff des Webservers auf die Methoden der Java-Objekte
kann auch aktiv genutzt werden. Dazu muss ein Servlet die vom Administrator gewünschte Aktion interpretieren und in einen Methodenaufruf umwandeln:
private void perfomAction(String action,
HttpServletRequest request) {
...
// Kick client from ${slot}.
if (action.equalsIgnoreCase("kick")) {
int slot = Integer.parseInt(request.getParameter("slot"));
User user = server.roster.all[slot];
server.roster.kick(user, "Be gone!");
return;
}
}
HTML Dateien bilden normalerweise die Grundlage der von Webservern
angebotenen Webseiten. Da die Inhalte der Webadmin-Seiten jedoch den
aktuellen Stand der Dinge widerspiegeln müssen, wurde ein Verfahren erarbeitet, das statische HTML Texte mit dynamischen, Java-ähnlichen Elementen mischt. Bevor eine solche Webseite an den Browser gesendet werden
kann, muss der Webserver die dynamischen Element auflösen und in normalen
HTML Text umwandeln. Diese Aufgabe übernimmt in dieser Implementierung das TemplateServlet, welches mittlerweile in das Groovy-Projekt aufgenommen wurde. Groovy ist eine an Java angelehnte Programmiersprache,
die auch zur Laufzeit interpretiert werden kann. Im folgenden Ausschnitt der
index.html Datei wird die Liste aller Benutzer mit Hilfe einer for-Schleife
aufgebaut. Dabei ist der Groovy Code entweder von den Markern <% %> eingerahmt oder, falls lediglich ein Textfragment generiert werden soll, mit ${}
KAPITEL 5. IMPLEMENTIERUNG
37
gekennzeichnet. Alle anderen Passagen stellen normalen, HTML formatierten
Text dar, der unverändert in das Zieldokument übernommen wird.
<html>
...
<h2>Roster</h2>
<dl>
<%
for (user in server.roster.all) {
%>
<dt>
#${user.getSlot()}
<b>${user.getName()}</b>
[<a href="/server?action=kick&slot=${user.getSlot()}">kick</a>]
</dt>
<dd>
${user.toString()}
</dd>
<%
}
%>
</dl>
...
</html>
Links
• http://jakarta.apache.org/commons/logging/
• http://logging.apache.org/log4j/
• http://jetty.mortbay.org/
• http://groovy.codehaus.org/
• TemplateServlet.java
KAPITEL 5. IMPLEMENTIERUNG
5.2
5.2.1
38
Umsetzung
CAPI: Die Command API
Das CAPI-Objekt listet alle verfügbaren Kommandos eines Spiels auf. Diese
Liste wird in der Initialisierungsphase der Applikation erstellt und muss auf
beiden Seiten, also beim Server und den Clients, identisch aufgebaut sein. Sie
stellt somit die zur Laufzeit unveränderliche Kernkomponente einer Applikation dar, die beim Versenden und Empfangen von Kommandos und bei der
Zuordnung von Kommados zu ihren execute()-Methoden eine zentrale Rolle
spielt.
Die CAPI Liste lässt sich in die zwei Teile Basis- und Spielkommandos spalten. Die Basiskommandos sind in der Datei
de.ctsr.mamuneen.Mamuneen deklariert und zur besseren Übersicht
ebenfalls nach ihrer Funktion getrennt. Der erste in Auszügen gedruckte
Funktionsblock dient hauptsächlich zum Testen der Implementierung und
nennt sich SystemCapi. Der zweite Block mit den Namen UserCapi gruppiert sämtliche Kommandoklassen, die zur Verwaltung der Benutzerlisten
benötigt werden.
interface Mamuneen {
interface SystemCapi {
class Beep extends VoidCommand {}
class Message extends StringCommand {...}
...
}
interface UserCapi {
class Quit extends VoidCommand {}
class Quitted extends UserCommand {...}
class JoinQuery extends StringCommand {...}
interface JoinReply extends Command {
class Failed extends ByteCommand implements JoinReply {}
class Succeeded extends AbstractCommand implements JoinReply {...}
}
class Joined extends UserStringCommand {...}
...
}
}
KAPITEL 5. IMPLEMENTIERUNG
39
Die Namen der Kommandoklassen geben, wie gefordert, Auskunft über
ihre Aufgaben und Einsatzgebiete. Zum Beispiel stellt das Kommando
UserCapi.Quit eine Aufforderung dar, den sendenden Client aus dem Spiel
zu entfernen. Da der Server den Client eindeutig über dessen Kanal bestimmt, braucht keine zusätzliche Information mitgesendet werden, die den
Client spezifiziert. Deshalb kann die datenlose Basisklasse VoidCommand erweitert werden. Nachdem der Server alle nötigen Änderungen an der Referenzsimulation vorgenommen hat, sendet er seinerseits das Kommando
UserCapi.Quitted an alle noch verbundenen Clients. Dieses Kommando
enthält ausschließlich die ID, die dem Client zugeordnet war, welcher das
Spiel soeben verließ. Mit den zwei Informationen Quitted und ID können
die Clientapplikation ebenfalls die nötigen Änderungen vornehmen.
Analog verhält es sich mit dem UserCapi.Joined Kommando. Der Server sendet damit die eindeutige ID, den Namen und weitere Daten über
einen neuen Mitspieler an die bereits spielenden Clients. Dem entsprechenden
UserCapi.Join Kommando wird ausserdem das reservierte Wort Query angehangen. Dadurch wird der Server angehalten eine Antwort, hier entweder
JoinReply.Succeeded oder JoinReply.Failed, an den sendenden Client
zu schicken. Normalerweise blockiert die Clientanwendung die Verarbeitung
parallel durch andere Server verschickter Kommandos solange, bis die einer
Query entsprechende Antwort Reply empfangen und ausgeführt wurde. Bevor der Server den Beitritt einen Mitspieler publiziert, überprüft dieser den
vom Client im JoinQuery codierten Benutzernamen und vergleicht das Passwort mit dem in der Datenbank gespeicherten Version. Bei einer negativen
Überprüfung der Angaben sendet der Server lediglich das Antwortkommando JoinReply.Failed an den sendenden Client. Andernfalls generiert der
Server eine JoinReply.Succeeded Antwort. Dieses Kommando enthält die
komplette Liste aller aktiven Spieler, inklusive dem neuen. Das macht ein
zusätzliches Senden eines UserCapi.Joined Kommados überflüssig.
Das Ausführen eines Kommandos in den Kommando-getriebenen Komponenten Server und Client geschieht durch einen Methodenaufruf innerhalb
der jeweiligen Komponente. Der nächste Abschnitt erläutert diesen Vorgang
genauer.
KAPITEL 5. IMPLEMENTIERUNG
5.2.2
40
Binden von Kommandoklassen und executeMethoden
Nachdem die CAPI-Liste fixiert ist, kann mit der Zuordnung der Kommandoklassen zu den entsprechenden Behandlungsmethoden begonnen werden.
Eine nötige Vorbedingung ist, dass jedes Kommando nur genau einmal in der
CAPI-Liste enthalten sein darf. Ist ein Kommando mehrfach aufgeführt, kann
keine eindeutige Zuordnung vorgenommen werden. Die Zuordnung schlägt
ebenfalls fehl, falls mehr als eine passende Behandlungsmethode für eine
Kommandoklasse existiert. Das ist der Fall, sobald zwei verschiedene Klassen jeweils eine Methode mit equivalenter Signatur deklarieren und beide als
Executoren bei der Applikation registriert sind.
Unter Umständen kann eine einzige Behandlungsmethode von mehreren
Kommandoklassen aus der CAPI-Liste als Ziel angesprungen werden. Dazu
muss die Methode einen nicht direkt instantiierbare Typ als Parameter erwarten. Das kann entweder ein Interface oder eine abstrakte Klasse sein. Da
diese beide Typen nicht explizit in der CAPI-Liste enthalten sein können,
muss jedes eingetrage Kommando der Liste auf Zuweisbarkeit geprüft werden. Jede ungebundene Kommandoklasse, die als Parameter der untersuchten Methode in Frage kommt, wird mit dieser Methode verbunden. Als Beispiel ist das Paar class JoinQuery und interface JoinReply zu nennen.
Zum JoinReply existiert eine Behandlungsmethode, die doppelt gebunden
ist: das Kommando JoinReply.Succeeded und JoinReply.Failed werden
beide von der selben Methode, nämlich void execute(UserCapi.JoinReply
reply), behandelt.
Durch diese Abbildung wird die folgende Struktur der Programmflusskontrolle nachgebildet und kann, wie danach beschrieben, durch eine generische
Version ersetzt werden:
void execute(Command command) {
if (command.getClass() == UserCapi.Joined) {
//
// create and add new user object to the world
//
}
else if (command.getClass() == UserCapi.Quitted) {
//
// remove specified user from the world
//
}
KAPITEL 5. IMPLEMENTIERUNG
41
...
else {
//
// command class not processed
//
}
}
Stattdessen übernimmt folgender Code die Aufgabe des Methodenaufrufs:
void execute(Command command) {
Method method = (Method) execmap.get(command.getClass());
if (method == null) {
throw new Error("Command class not bound.");
}
method.invoke(command);
}
/**
* Create and add new user object to the world.
*/
void execute(UserCapi.Joined command) {
...
}
...
Die Erstellung der im zweiten Ausschnitt verwendeten Variablen execmap
geschieht mithilfe der Java-internen Reflektionswerkzeuge aus dem Paket java.lang.reflect. Der Einsatz einer auf Hashing basierenden
Map-Implementierung garantiert einen konstanten und gleichzeitig sehr geringen Zeitaufwand zum Identifizieren der zur Kommandklasse passenden
Methode. Mehr Information können dem beiliegenden Quellcode entnommen
werden.
KAPITEL 5. IMPLEMENTIERUNG
42
Der Vorteil dieses generischen Verfahrens liegt zunächst in der besseren Übersicht durch die Trennung der Behandlungsquellcode in verschiedene
Methoden. Zum Einen wird damit eine potentielle Spaghetticode-Methode
verhindert. Man stelle sich die oben zuerst angeführte execute-Methode
für mehr als 100 Kommandos vor. Zum Anderen erleichtern moderne Java
Entwicklungsumgebungen den Programmierern das Auffinden von Methoden
durch Baumansichten und andere visuelle Komponenten. Der größte Vorteil
liegt jedoch in der einfachen Erweiterbarkeit des Systems durch neue Kommandos und ihren entsprechenden Behandlungsmethoden. Aufgrund des dynamischen Bindens muss kein bestehender Quellcode geändert und neukompiliert werden.
KAPITEL 5. IMPLEMENTIERUNG
5.2.3
43
Java NIO Selector
Die Transceiver sind an einer performanzkritischen Position des Designs angesiedelt. Deshalb wurde bei der Implementierung ein besonderes Augenmerk
auf die Verwendung systemnaher Verfahren und Methoden gelegt.
Das Paket java.nio bietet seit der Version J2SE 1.4 Unterstützung für
gemultiplexte und nicht blockierende I/O Operationen. Die dort angestammten Klassen und Funktionen werden in den Serverkomponenten verwendet.
Durch den Einsatz von Selektoren, selektierbaren Kanälen und Selektionsschlüsseln können zum Beispiel die Transceiver mehr Clients verwalten als
mit einem Thread-orientierten und blockierenden Ansatz. Hierbei müssen
pro Client zwei Threads gestartet und abgestellt werden, die die lesende und
schreibende Kommunikation mit die Socketverbindung übernehmen. Das ist
problematisch, da die Anzahl der Threads, die ein Betriebsystem effizient
verwalten kann, limitiert ist. Gerade auf Maschinen mit nur einem Hauptprozessor kann der Zeitaufwand der Threadverwaltung sehr hoch werden und
somit das komplette System ausbremsen.
Das hier vorgestellte Verfahren kommt im Minimalfall mit lediglich einem
Thread aus. Dabei können hunderte Clients effizient und betriebsmittelschonend mittels eines multiplexenden Selektors verwaltet werden. Das Selektorobjekt bietet dafür unter anderem eine blockierende Methode select() an.
Der aufrufende Thread verweilt solange in dieser Methode, bis mindestens
ein Kanal Behandlung erfordert.
Ein Transceiver erzeugt zunächst einen ServerSocketChannel. Diesen
Kanal, genauer das darunter liegende ServerSocket Objekt, bindet der Transceiver an eine wohl-bekannten Portnummer und versetzt ihn in den nicht blockierenden Kommunikationsmodus. Den so konfigurierten Kanal registriert
der Transceiver in einem bereits geöffneten Selektor. Außerdem wird bei der
Registrierung eines selektierbaren Kanals festgelegt, bei welchen Operationen der Selektor Arbeit signalisieren soll. Es gibt vier Operationen, die in
der Klasse java.nio.SelectionKey als Konstanten definiert sind:
OP_ACCEPT, OP_CONNECT, OP_READ und OP_WRITE
Es können, sofern der Kanal diese zulässt, auch beliebige Operationskombinationen gebildet werden. Die Menge an Operationen, die beim Registrieren
angegeben wird, nennt sich interest set. Zum Beispiel ist OP_ACCEPT die einzig
gültige Operation der ServerSocketChannel Klasse.
Sobald ein Client versucht eine Verbindung zu dem wohlbekannten Port
aufzubauen, signalisiert das der Selektor, in dem er den bei der Registrierung erzeugten Selektionsschlüssel in der Menge der selektierten Schlüssel an
den Transceiver zurückgibt. Dabei kann jeder Schlüssel eine beliebige, von
KAPITEL 5. IMPLEMENTIERUNG
44
Null verschiedene Menge an fertigen Operationen enthalten. Diese Menge
nennt sich ready set. In diesem Fall kann auf dem ServerSocketChannel
die Methode accept() aufgerufen werden, die als Resultat einen normalen
SocketChannel liefert. Das ist nun der serverseitige Endpunkt der Socketverbindung zu einem neuen Client. Bevor dieser Kanal dem Selektor hinzugefügt
werden kann, muss er ebenfalls in den nicht blockierenden Kommunikationsmodus geschaltet werden. Da über diesen Kanal Daten vom Client gelesen
und an den Client versendet werden sollen, wird bei der Registrierung sowohl
OP_READ als auch OP_WRITE angegeben.
Sobald ein Client Daten an der Server geschickt hat, selektiert der Selektor den entsprechenden Selektionsschlüssel für das ready set und setzt den
Schalter für die Leseoperation OP_READ. Man sagt, dass der Schlüssel lesbar
ist. Es wird jedoch keine Aussage über die Datenmenge gemacht, sondern
nur darüber informiert, dass Daten an dem dem Schlüssel zugeordneten Kanal zum Lesen bereit stehen. Diese können nun durch den Selektionsthread
gelesen und weiterverarbeitet werden.
Nachdem alle momentan vorliegenden Daten an einen bestimmten Client
versendet wurden, muss der Schalter OP_WRITE aus dem interest set des Selektionsschlüssels entfernt werden. Das verhindert eine Selektion des Schlüssels beim nächsten Aufruf von select(). Sollen weitere Daten an den Client
geschickt werden, muss der Schalter erneut gesetzt werden. Dazu bietet die
Klasse SelectionKey die Methode interestOps(int).
Sollten beim Akzeptieren, Lesen oder Schreiben Fehler auftreten, kann das
auf eine vom Client aus unterbrochene Verbindung hinweisen. Diese Ausnahmefälle werden, wie in Java üblich, durch die Klasse java.io.IOException
dargestellt und von der Implementierung abgefangen. Mit dem Schließen eines Kanals werden die entsprechenden Selektionsschlüssel ungültig und automatisch aus der Menge der vom Selektor beobachteten Schlüssel entfernt.
Bei diesem Vorgang generiert die Java Implementierung der Firma Sun in
der Version 1.4 für Windows einen Zugriff auf eine ungültige Referenz. Dieser Fehler ist im Kapitel 7 detailierter beschrieben.
Kapitel 6
Ein Demospiel
6.1
Design
Im Folgenden wird das Konzept des Demospiels vorgestellt.
6.1.1
Allgemein
Namensfindung
Das Spiel wird auf Run To The Hills getauft. Dieser Titel beruht einerseits
auf der hügeligen Beschaffenheit des Spielareals, andererseits auf dem gleichnamigen Song der Band Iron Maiden aus dem Jahre 1982.
Hintergrund
Verschiedene Fraktionen wollen einen Planeten besiedeln. Dazu haben sie
jeweils eine stationäre, nicht einnehmbare Basis auf der Oberfläche errichtet.
Von dieser aus entsenden sie holographische Projektionen ihrer selbst, um
Territorium für sich einzunehmen.
Ziel
Die Spieler versuchen Flaggenpunkte der gegnerischen Teams einzunehmen
und die eigenen Flaggen zu verteidigen. Punkte sammeln sie, indem sie Flaggen zum eigenen Team umfärben oder andere Spieler zu ihren Spawnpunkten zurückschicken. Das Team, welches als erstes eine bestimmte Punktezahl
erreicht, gewinnt die laufende Runde. Ist in einer Welt eine voreingestellte
Anzahl von Runden gespielt worden, beginnt ein neues Spiel in einer neuen
Welt.
45
KAPITEL 6. EIN DEMOSPIEL
46
Ein möglicher Spielablauf
Der Spieler startet die Client Applikation und wird nach seinem Namen gefragt und nennt sich Dude. Zusätzlich kann er noch weitere Einstellungen, die
seinen Rechner betreffen, angeben. Danach verbindet er sich mit einem ihm
bekannten Server, auf dem bereits ein Spiel im Gange ist. Es befinden sich 23
Spieler, aufgeteilt in drei Teams (Rot, Grün und Blau) auf dem Server. Da
Team Grün zur Zeit zahlenmäßig unterlegen ist, wird Dude automatisch diesem Team zugewiesen. Jetzt wählt Dude seine Rolle, die er im Spiel ausüben
will. Er entscheidet sich für den schnellen jedoch mit schwachem Energieschild ausgerüsteten Angreifer. Außerdem bestimmt er Flagge 4 als seinen
Startpunkt, da Team Grün bereits mehrere Flaggen kontrolliert. Er wartet
die verbleiben Sekunden seiner Spawnzeit ab. [. . . ] Dude erscheint in der
Nähe der Flagge 4. Diese befindet sich auf einer kleinen, bewaldeten Insel.
Zusammen mit zwei Teamkollegen watet er durch das seichte Küstenwasser
in Richtung Flagge 6, welche gerade von fünf Spielern aus Team Rot kontrolliert wird. Der folgende Ansturm ist kurz und erfolglos: Dude und seine
Mitangreifer werden durch Antiprotonenstrahlen aufgelöst. Die drei glücklosen Angreifer müssen erneut einen Startpunkt und eine Rolle wählen, sowie
einige Sekunden auf die Rekalibrierung ihrer Projektionsmatrix warten. [. . . ]
Eine halbe Stunde später endet die Runde mit einem Sieg von Team Rot!
6.1.2
Anforderungsanalyse
√
und × siehe Abschnitt 6.2.
Zur Bedeutung der Symbole
Allgemein
• Große, zufällige Spielwelten
• Viele Spieler
√
√
• Mehr als zwei Teams
√
• Spieler sind unbelebte Hologramme
√
• Spieler starten bei festgelegten Flaggenpunkten
√
• Gegnerische Flaggenpunkte einnehmen um Punkte zu erzielen
• Eigene Flaggenpunkte verteidigen
√
• Anzahl der Flaggen richtet sich nach Weltgröße ×
√
KAPITEL 6. EIN DEMOSPIEL
47
• Einteilung in Runden und Welten ×
• Möglichkeit, andere Spieler zu einem
√Spawnpunkt (Startpunkt) zurückzuschicken um Punkte zu erzielen
• Verschiedene Spielerrollen ×
• Verschiedene Ausrüstungsgegenstände ×
• Bonuskisten ×
• Chatfunktion (Texte versenden, global oder teamintern)
√
• Teaminterne Schnellkommandos per Tastenkombination ×
• Punktelisten / Highscore, sowohl für Team als auch Einzelspieler ×
• Statistiken ×
• Continuous World (Spiel ist immer offen)
√
• Spectators (Zuschauer) sind erlaubt ×
• Verschiedene Serveroptionen
√
• Verschiedene Clientoptionen, wie Bildschirmauflösung und Farbtiefe
Der Server
Bei Serverstart werden unter anderem festgelegt:
• Weltdimension, z. B. 2562 Tiles
√
• Maximale Spielerzahl (Kapazität), z. B. 128
• Anzahl der Teams, zwischen 2 und 8
√
√
• Maximale Punkte pro Runde, z. B. 100 ×
• Runden pro Welt, z. B. 3 ×
• Friendly Fire-Anteil, z. B. 50% ×
Zur Laufzeit stehen folgende Funktionen zur Verfügung:
√
KAPITEL 6. EIN DEMOSPIEL
48
Administration
Über ein Webfrontend können Spieler in andere Teams eingeteilt, oder
vom Server gekickt (entfernt) oder gebanned (dauerhaft ausgeschlossen) werden.
√ Außerdem können Mitteilungen an die Spieler gesendet
werden.
Auto Balancing
Bei Eintritt in die Welt wird dem neuen Spieler ein Team vorgeschlagen.
√
Dieser Vorschlag richten sich nach der aktuellen Teambalance.
Die Welt
• Sämtliche Eigenschaften werden zufällig generiert
√
• Verschiedene Themen, wie Mond, Erde und Fantasiewelt ×
• Verschiedene Landschaften, am Beispiel Erde:
– Animiertes Wasser (Meere, Flüsse, Seen)
– Küste
√
– Grasland
– Wald
√
√
√
– Gebirge
√
• Stufenloses Höhenprofil
√
• Wege und Straßen ×
• Einfluss der Oberflächenbeschaffenheit auf den Spielfluss ×
• Persistente Veränderungen von Höhenprofil und Oberflächen ×
• Animierbare Objekte, wie Spieler, Bäume und Kisten
Der Spieler
• Name
√
• Teamzugehörigkeit / Farbe
• Punkte pro Runde ×
• Rang pro Welt ×
√
√
KAPITEL 6. EIN DEMOSPIEL
• Position in der Welt (x, y, z)
• Ausrichtung
49
√
√
• Geschwindigkeit ×
• Rolle, wie Angreifer und Verteidiger ×
• Ausrüstung, wie Waffen und Schutzschild ×
• Hitpoints
√
Das Team
• Farbe, ev. auch Wappen
√
• Punkte pro Runde ×
• Gewonnene Runden pro Welt ×
• Homebase, Holocore,
Heimatpunkt. Dieser ist nicht von anderen Teams
√
einnehmbar
Punktewertung
Es gibt zwei Möglichkeiten, Punkte zu erlangen:
1. Desintegrieren eines Gegners
Für den Spieler 1 Punkt, für das Team ebenfalls 1 Punkt ×
2. Umfärben einer Flagge
Für den Spieler 2 Punkte, für das Team sogar 2 Punkte pro beteiligtem
Spieler ×
6.2
Implementierung
Wie in der Aufgabenstellung festgelegt, wurde das Demospiel in Zusammenarbeit mit Thomas Schuster entwickelt, auf dessen Diplomarbeit in diesem
Zusammenhang verwiesen wird.[Schu04] Aufgrund der begrenzten Zeit, die
für die Implementierung zur Verfügung stand, konnten nicht alle der angedachten Spielinhalte verwirklicht werden. Um zu zeigen, welche Zielsetzungen
aus dem vorigen Abschnitt erreicht wurden und√
welche nicht, sind diese mit
entsprechenden Symbolen gekennzeichnet. Ein
steht für eine erfolgreiche
Umsetzung, ein × für ein Fehlen innerhalb des Spiels. Für anspruchsvollere
KAPITEL 6. EIN DEMOSPIEL
50
Graphiken, d. h. Texturen und Sprites, stand ebenfalls nicht genug Zeit zur
Verfügung. Beispielsweise sollte die Spielfigur statt der Mensch-ärger-dich”
nicht“-Variante flüssig animiert und in verschiedenen Rotationen gerendert
sein.
6.3
Screenshots
Die folgenden Screenshots wurden bei einer Auflösung von 1024x768 Pixeln
und 32 bit Farbtiefe aufgenommen. Sie sollen den Funktionsumfang und die
Qualität von Run To The Hills im Bereich der Graphik demonstrieren.
KAPITEL 6. EIN DEMOSPIEL
51
Abbildung 6.1: Übergang von seichtem in tiefes Wasser. Der Strand besteht
aus zwei unterschiedlichen Landtypen (trockener und feuchter Sand). Dazu kommt ansatzweise Gras als dritter Landtyp. Unter dem Wasser liegt in
Küstennähe der feuchte Sand, weiter draußen ein dunkler Meeresgrund als
insgesamt vierter Landtyp in diesem Bild
KAPITEL 6. EIN DEMOSPIEL
52
Abbildung 6.2: Strand und seichtes Wasser. Die verwendeten Landtypen entsprechen denen aus Abbildung 6.1
KAPITEL 6. EIN DEMOSPIEL
53
Abbildung 6.3: Übergang von Strand nach Wald. Der Wald besteht aus statischen Sprites (inklusive Schatten per Alphakanal) und Gras als Landtyp
Kapitel 7
Ergebnisse
Die in dieser Arbeit vorgestellte Architektur einer Massive Multiplayer Online Game Engine erfüllt die gestellten Anforderungen. Dazu gehören unter
anderem die geringen Antwortzeiten, die Verwaltung vieler, konkurierender
Spieler und nicht zuletzt die einfache Integration in ein Demospiel.
• Echtzeit: Durch die unmittelbare Übertragung der Kommandos von
den Clients zum Server und umgekehrt kann das Echtzeitgefühl der
Spieler gewährleistet werden.
• Gleichzeitigkeit: Das Design von Mamuneen ermöglicht sehr vielen Spielern gleichzeitig in einer Welt zu interagieren. Leider konnten genaue
Messungen der serverseitigen Belastbarkeit aus Zeitgründen nicht angefertigt werden.
• Skalierbarkeit: Der Einsatz nicht-blockierender Ein- und Ausgabetechnologie bietet allerdings eine resourcenschonende und schnelle Basis für
hunderte von Clientverbindungen. Durch das mehrschichtige Design der
Serverkomponenten kann die Last gut aufgeteilt werden.
• Klarheit und Abstraktheit: Es wurde beim Design und während der
Implementierung die Verwendung von Design Patterns und deren Nomenklatur verfolgt. Zudem können die Softwaremodule durch die strikte Trennung ihrer Schnittstellenbeschreibungen (interfaces) problemlos
ausgetauscht werden.
• Referenzimplementierung: Im Rahmen dieser Arbeit wurde das Design
prototypisch implementiert und erfolgreich im Demospiel zum Einsatz
gebracht.
54
KAPITEL 7. ERGEBNISSE
55
• Erweiterbarkeit: Die Erweiterbarkeit demonstriert das in Rahmen dieser Diplomarbeit erstellte Spiel Run To The Hills: Das Demospiel
kann die Leistungsfähigkeit beider Frameworks (Mithril [Schu04] und
Mamuneen) eindrucksvoll unter Beweis stellen. Das Spiel läuft auf unterschiedlichster Hardware bis hin zu Laptops mit Graphikchips, deren
3D-Tauglichkeit bzw. Graphikleistung im Allgemeinen als eher zweifelhaft anzusehen ist. Dies kann zusätzlich als eine Erfüllung des Neben”
ziels“, auch weniger leistungsstarke Graphikhardware zu unterstützen,
gewertet werden.
• Robustheit: Die Wahl des verbindungsorientierten und zuverlässigen
TCP/IP Protokolls sichert in der prototypischen Implementierung die
Übertragung der Kommandos. Ausfälle einzelner Knoten werden erkannt und behandelt.
• Laufzeitstatistik: Es wird sowohl serverseitig als auch bei den Clients
eine Reihe an Kenngrößen ermitteln. Anhand dieser Werte kann eine
Anpassung des Systems zur Laufzeit vorgenommen werden.
– Application: Gesamtzahl der bisher ausgeführten Kommandos
– Application: Anzahl der in der letzen Sekunde ausgeführten Kommandos
– Proxy/Client: Round Trip Time/Ping eines Kommandos
– Proxy/Client: Anzahl der gesendeten/empfangen Kommandos
• Kontrolle: Der integrierte Webserver verschafft den Administratoren
Einblicke in das laufenden System und ermöglicht Veränderungen vorzunehmen. Der nächste Abschnitt beschreibt die Integration im Detail.
KAPITEL 7. ERGEBNISSE
7.1
56
Serveradministration via Webbrowser
Die folgenden Abbildungen zeigen das integrierte Webinterface. Die Implementierung ist in Abschnitt 5.1.2 beschrieben.
Abbildung 7.1: Serveradministration via Webbrowser – Kein Spieler ist online. Es werden lediglich Basisinformation im Browserfenster angezeigt. Der
Server wurde mit einer maximalen Kapazität von 8 gestartet.
KAPITEL 7. ERGEBNISSE
57
Abbildung 7.2: Serveradministration via Webbrowser – Mittlerweile sind vier
Benutzer eingeloggt, die in einer Liste dargestellt werden. Zusätzlich werden
technischen Angaben wie Laufzeitnummer und entfernte Internetadresse pro
Spieler aufgeführt. Die serverseitigen Aktionen für den Administrator sind
freigeschaltet. Neben der Textsendefunktion say kann er außerdem auch beliebige Benutzer aus dem Spiel entfernen. Dazu dient ein Hyperlink namens
[kick], der für jeden Benutzer generiert wird.
KAPITEL 7. ERGEBNISSE
58
Abbildung 7.3: Serveradministration via Webbrowser – Es sind nur noch
zwei Benutzer verbunden. Die beiden anderen wurden vom Administrator
gekickt“.
”
KAPITEL 7. ERGEBNISSE
7.2
59
NIO: Fehler in Sun JDK 1.4 für Windows
Im Laufe der Implementierung des Transceivers kam es zu unvorhersehbaren
Problemen und Fehlermeldungen. Dabei handelte es sich um einen sogenannte NullPointerException, die auf eine ungültige Objektreferenz hinweist:
java.lang.NullPointerException
at sun.nio.ch.WindowsSelectorImpl$SubSelector.processFDSet
(WindowsSelectorImpl.java:309)
at sun.nio.ch.WindowsSelectorImpl$SubSelector.processSelectedKeys
(WindowsSelectorImpl.java:282)
at sun.nio.ch.WindowsSelectorImpl$SubSelector.access$2600
(WindowsSelectorImpl.java:245)
at sun.nio.ch.WindowsSelectorImpl.updateSelectedKeys
(WindowsSelectorImpl.java:427)
at sun.nio.ch.WindowsSelectorImpl.doSelect
(WindowsSelectorImpl.java:142)
at sun.nio.ch.SelectorImpl.lockAndDoSelect
(SelectorImpl.java:59)
at sun.nio.ch.SelectorImpl.select
(SelectorImpl.java:70)
at sun.nio.ch.SelectorImpl.select
(SelectorImpl.java:74)
at de.ctsr.mallorn.server.socket.SocketReceiver.run
(MallornSocketReceiver.java:120)
at java.lang.Thread.run(Thread.java:534)
Diese Probleme tauchten zuerst unregelmäßig auf und schienen die Stabilität
des Systems nur manchmal zu beinträchtigen. Mit steigender Benutzeranzahl
häuften sich jedoch die serverseitigen Fehlerfälle und machten eine genauere
Investigation unumgänglich. Nach einer gründlichen Untersuchung der eigenen Quellcodezeilen konnte ein Fehler des Autors bei der Anwendung der
NIO Klassen ausgeschlossen werden. Das Problem war somit innerhalb einer
der NIO Klassen anzusiedeln. Verstärkt wurde dieser Verdacht durch den
Fakt, dass die Fehler abhängig von der verwendeten Java Runtime Version
und des zugrundeliegende Betriebssystem auftraten. Die betroffenen Systeme
sind:
• Sun JDK 1.4.2 03 (client) auf Windows 2000 (SP4)
• Sun JDK 1.4.2 04 (client) auf Windows 2000 (SP4)
• Sun JDK 1.4.2 04 (client) auf WindowsXP (SP1 + Hotfixes)
KAPITEL 7. ERGEBNISSE
60
• Sun JDK 1.4.2 05 (client) auf Windows 2000 (SP4)
• Sun JDK 1.4.2 05 (client) auf WindowsXP (SP1 + Hotfixes)
Keine Probleme machten folgende Kombinationen:
• IBM JDK 1.4.1 IBM build 20040301a auf Windows 2000 (SP4)
• Sun JDK 1.4.2 04 (client) auf Linux (Kernel 2.6.6-rc2)
• Sun JDK 1.4.2 06 (client) auf Windows 2000 (SP4)
• Sun JDK 1.5.0 beta1 (client) auf Windows 2000 (SP4)
• Sun JDK 1.5.0 beta2 (client) auf Windows 2000 (SP4)
• Sun JDK 5 (client) auf Windows 2000 (SP4)
Zur Eingrenzung der fehlerhaften Codestelle wurde eine Applikation programmiert, die aus einem Server und einem Client bestand. Die Clientinstanzen konnten mit dem Server verbunden werden und einwandfrei Kommandos
austauschen. Sobald jedoch einer der Clients die Verbindung beendete, stellte
der selektierende Serverthread mit der oben genannten Meldung seine Arbeit
ein. Damit konnten keine Daten mehr übertragen werden.
Beide offensichtlichen Lösungen der Situation, der Wechsel zu Linux als
Serverplatform und der Einsatz des sich zu diesem Zeitpunkt noch in der
Entwicklung befindlichen JDK 1.5, wurden verfolgt. Die Fehlerdatenbank
von Sun (http://bugs.sun.com/bugdatabase/) listet seit Oktober 2004 unter
anderem folgende kürzlich gelöste Probleme:
4892104: Null pointer exception while deregistering key from selector
4729342: Selector.select() throws CancelledKeyException
KAPITEL 7. ERGEBNISSE
7.3
61
Fazit und Ausblick
Das in dieser Diplomarbeit behandelte Gebiet der Onlinecomputerspiele für
viele Spieler vereinigt interessante Spezialbereiche der Informatik samt ihrer Probleme und Herausforderungen. Aus der Kombination der jeweiligen
Lösungen und Verfahren konnte ein System entwickelt werden, das einen robusten Rahmen für weitere Forschungsarbeiten dargestellt:
Allgemeine Optimierungen An vielen Stellen der prototypischen Implementierung wurden die Voreinstellungen der Komponenten verwendet. Dazu
zählen die verwendete Virtual Machine, Threadprioritäten, Socketoptionen,
Puffer- und Queuegröße und andere laufzeitkritische Einstellungen.
Interessenmanagement Aura, Fokus, Nimbus . . . Hierbei wird serverseitig eine Reduktion der Zielknoten (Clients) einer Nachricht vorgenommen.
Es macht zum Beispiel keinen Sinn die Bewegungsaktionen zweier Spieler zu
übermitteln, die sehr weit voneinander entfernt sind. Unter der Vorraussetzung, dass sich die Spieler möglichst gleichmäßig in der Welt verteilen, lässt
sich durch ein geschicktes Interessenmanagement die Datenmenge, die Server
an die Clients senden muss, stark verringern.
Hochperformante Datenbanken Der Einsatz leistungsstarker und dynamisch skalierender Datenbanksysteme kann die Antwortzeit der Serverkomponenten reduzieren. Sofern genügend Arbeitsspeicher zur Verfügung steht,
sollten vor allen Dingen die sogenannten in-memory databases einen extremen Geschwindigskeitszuwachs liefern.
Literaturverzeichnis
[Bliz]
Blizzard. Blizzard Entertainment. 5
[Gamo]
Gamona.de. Interview mit Chris Sigaty, 14.09.04, World of Warcraft. 5
[GHJV95] Erich Gamma, Richard Helm, Ralph Johnson und John Vlissides.
Design Patterns: Elements of Reusable Object-Oriented Software.
Addison-Wesley Professional. 1995. 24, 31
[Mets02]
Steven John Metsker. Design Patterns Java Workbook. AddisonWesley. 2002. 31
[RT98]
Newsgroup RT. Realtime Computing FAQ. Technischer Bericht
Version 3.5, comp.realtime, July 1998. This posting provides an
overview of newsgroup comp.realtime by summarizing the history,
common past topics, and frequently asked questions. 7
[Schu04]
Thomas Schuster. Entwicklung einer isometrischen Grafik-Engine
in Java, 2004. Diplomarbeit. Universität Koblenz-Landau, Standort Koblenz. 49, 55
[SiZy99]
Sandeep Singhal und Michael Zyda. Networked Virtual Environments – Design and Implementation. Addison-Wesley Professional. 1999. 11
[Steu03]
Hartwig Steusloff. Verteilte Echtzeitsysteme und Eingebettete
Systeme - Über Systeme und Technologien. In Peter Holleczek
und Birgit Vogel-Heuser (Hrsg.), Verteilte Echtzeitsysteme. GI,
Springer, 2003, S. 1–12. Fachtagung der GI-Fachgruppe 4.4.2,
Echtzeitprogrammierung und PEARL (EP). 8
[Sun 04]
Inc. Sun Microsystems. Sun Game Server Technology – An Executive Overview. Technischer Bericht, 2004. 16
62