Zum besseren Verständnis wird zunächst nochmals der Klassenaufbau der Server-Komponente aus Kapitel 3.6 angegeben. Diese Hierarchie muss um zwei Klassen erweitert werden, um die Netzwerkfunktionalität des Servers und um die Klasse Daten. Der wesentliche Unterschied zur Darstellung in Kapitel 3.6 besteht darin, dass nun anstelle der lediglich skizzierten Aufrufe der Netzwerkfunktionen die tatsächliche Implementierung erfolgt. Im Folgenden wird zunächst die main-Methode der Klasse betrachtet, die den Server selbst steuert. Abb. 7.1: Klassenhierarchie der Server-Komponente import java.awt.*; import java.net.*; import java.io.*; public class Server { static ServerSpiel ss; static SVServer network; public static void main(String[] arguments) throws IOException { while (true) { try { network = new SVServer(); System.out.println("Netzwerk erstellt"); ss = new ServerSpiel(); System.out.println("Spiel initialisiert"); } catch (IOException e) { System.out.println("IO-Exception!"); } spiele(); } } Die Funktionalität dieser Methode ist leicht zu verstehen. In einer while-Schleife werden Anfragen von Clients verarbeitet. Hierbei wird zunächst eine Netzwerkanbindung erstellt. Anschließend wird die Methode spiele aufgerufen, in der die eigentliche Spielfunktionalität enthalten ist. Zu Beginn dieser Methode wartet der Server auf Daten vom Client. Hierbei wird stets davon ausgegangen, dass der Client das Spiel beginnt. static void spiele () throws IOException{ boolean serverDran=false, spielEnde=false, einTreffer=false, zweiTreffer=false; Point koord = new Point(0,0); Daten daten; //Warten, bis der Client eine Position (x,y) schickt daten = network.getData(); Anschließend wird ein Spieldurchlauf in einer while-Schleife ausgeführt. Hierzu werden zunächst Schüsse des Clients verarbeitet. while (spielEnde==false) { if (serverDran ==false) { if (ss.rechnerTreffer(daten.p)) { //Wir sind getroffen, teile dies dem Client mit System.out.println("Wir sind getroffen"); daten.status=daten.TREFFER; network.sendData(daten); serverDran=false; if (ss.schiffZahlServer==0) { //Wir haben verloren???? spielEnde=true; network.beenden(); System.out.println("Server hat verloren"); return; } //Erwarte naechsten Schuss daten = network.getData(); } else serverDran=true; } Ist der Server an der Reihe, so werden Koordinaten zunächst nach dem Zufallsprinzip ausgewählt, um einen ersten Treffer erzielen zu können. else { //Jetzt schiessen wir if (einTreffer==false){ koord=ss.schiesseInsBlaue(); einTreffer=true; //Frage den Client ob Treffer. daten.status=daten.SCHUSS; daten.p=koord; network.sendData(daten); daten = network.getData(); if (daten.status != daten.TREFFER) { einTreffer=false; serverDran=false; ss.spieler.spiel[koord.x][koor d.y]=ss.spieler .WASSER; } else ss.schiffZahlClient--; } Hat der Server eine Koordinate eines gegnerischen Schiffs als Anhaltspunkt, so wird im Folgenden nach einer zweiten Koordinate gesucht, um die Richtung des Schiffs bestimmen zu können. if ((zweiTreffer==false)&& (einTreffer==true)) { koord=ss.sucheZweitenTreffer(); zweiTreffer=true; //Frage den Client ob Treffer. daten.status=daten.SCHUSS; daten.p=koord; network.sendData(daten); daten = network.getData(); if (daten.status != daten.TREFFER) { // if Treffer beim Client: Richtung des //Schiffes jetzt bekannt. zweiTreffer=false; serverDran = false; ss.spieler.spiel[koord.x][koor d.y]=ss.spieler .WASSER; } else ss.schiffZahlClient--; } Generell werden die Sprünge in die geeignete Schleife mit Hilfe der Variablen einTreffer und zweiTreffer realisiert. Sind zwei Koordinaten eines Schiffs bekannt, so kann dieses versenkt werden, weil die Richtung bekannt ist. Hierbei ist zu beachten, dass die Information, ob ein Schiff bereits versenkt ist, nicht mitgeteilt werden muss. Der Server muss diese Information daher durch geeignete Schüsse herausfinden. Ein Schiff ist dann versenkt, wenn keine Schusskoordinaten mehr gefunden werden können, die zu einem Schiff gehören könnten. if ((zweiTreffer==true)&& (einTreffer==true)) { //Abschuss koord=ss.schiffVersenken(); if (koord == null){ //Schiff versenkt einTreffer=zweiTreffer=false; } else { //Frage den Client ob Treffer. daten.status=daten.SCHUSS; daten.p=koord; network.sendData(daten); daten = network.getData(); if (daten.status != daten.TREFFER) { serverDran = false; ss.spieler.spiel[ss.koord inate3.x][ss.koordinate3. y]= ss.spieler.WASSER; ss.koordinate3=null; } else ss.schiffZahlClient--; } } Nachdem ein Schiff versenkt wurde, muss überprüft werden, ob der Gegner noch über Schiffe verfügt. //Sind wir immer noch dran? if (ss.schiffZahlClient<=0){ //gewonnen spielEnde=true; System.out.println("Server hat gewonnen"); network.beenden(); return; } }// } //while spiel } //Methode spiel }//Klasse Es wurde bereits mehrfach betont, dass das Ziel der Erstellung dieser Anwendung darin besteht, Komponenten so zu entwickeln, dass sie ohne Modifikationen größeren Umfangs kombiniert werden können. Eine Änderung der anderen Klassen des Servers ist daher nicht notwendig.