# Spiel netzwerkfähig machen (RMI)



## SebiB90 (30. Jan 2008)

Hi,

ich hab das Spiel "Schiffe Versenken" vor kurzem programmiert und wollte es netzwerkfähig machen und dabei gleich auch mal RMI lernen. Denke das wäre die beste möglichkeit.

Zunächst mal: Habt ihr allgemeine Tipps für mich, wie man ein Spiel netzwerkfähig macht? Wie man an sowas anstellen soll? Bitte keine Links zu Standard Erklärungen von RMI wie z.b "Java ist eine Insel". Kapitel hab ich schon kurz überflogen und morgen wirds genau gelesen 

Folgende Fragen kamen mir bisher:

1. Wie kann ich ein broadcast realisieren? Also, ich mach ein Server auf im Netzwerk, der Client kann dann eine Liste von allen Servern bekommen. Wie sowas realisierbar wäre, wurd mir bisher nicht klar. Es gibt ja eine Methode Registry.list(), aber der muss man ja auch den Server übergeben und man bekommt doch nur die Verfügbaren Remote Objekte.

2. Angenommen ich hab eine Remote Interface Methode addXXXListener(XXXListener l). Ich rufe vom Client aus die Methode auf und übergebe ein Listener. Auf dem Server wird dann der Listener irgendwann mal benachrichtigt. Wird dann auch auf dem Client die Methode des Listeners ausgeführt oder bekommt der Server nur eine Kopie des Objektes und auf dieses Objekt wird die Methode angewendet?
Wenn letzteres der Fall ist, wie kann man das Problem lösen?

Danke schonmal für die Antworten  
SebiB90


----------



## Guest (30. Jan 2008)

1) Broadcast mit RMI gibt's nicht... zumindest noch nie gehört. 
2) Siehe RMI Callback. Siehe z.B. hier: http://www.java-forum.org/de/viewtopic.php?t=24651


----------



## tuxedo (31. Jan 2008)

zu 1) In der Tat. Broadcast ist keine so gute Idee. Lieber einen zentralen Server basteln der eine Spiele-Server-Liste bereit hält und wo sich Spiele-Server anmelden können. 

zu 2) Achtung bei Callbacks: Solltest du dein Spiel nicht nur im lokalen Netzwerk benutzen wollen, sondern auch im Internet, so stehst du vor dem Problem, dass die Callbacks, die der Server zum Client hin macht, eine neue TCP-Verbindung benötigen. D.h. der Server versucht eine neue Verbindung zum Client aufzubauen. Und wenn der Client dann hinter einem Router oder einer Firewall sitzt, klappt das nicht. Wäre ja nur halb so tragisch wenn man nur den Router oder die Firewall des Server-Rechner einstellen müsste. Aber bei den RMI-Callbacks ist hier nunmal leider auch der Client vom gleichen Problem betroffen. Sofern du nur lokal im Netzwerk spielen willst gibts also keine Probleme. Ansonsten kannst du dir ja mal mein "SIMON" Projekt (siehe links in meiner Signatur) anschauen.

- Alex


----------



## SebiB90 (31. Jan 2008)

Thx schonmal für die Antworten.

Mit nem zentralen Server wird wohl eher nix. Da es nur nen kleines Netzwerkspiel sein soll und extra nen Server für die anderen Server bissel übertrieben und wo sollte dieser auch laufen. Müsste ja ne feste ip bzw dnsalias haben.

Hab mir das mal mit den Callbacks in dem Thread angeschaut und versuch zu implementieren als kleinen Test. Nur das Problem bei mir ist. Das wenn die Implementierung von dem Listener nicht von UnicastRemoteObject erbt, sondern mit der export Methode zu einem RemoteObject wird, dann diese Exceptions kommt. Nur keine Ahnung wieso:


```
Exception in thread "main" java.rmi.StubNotFoundException: Stub class not found: client.Client_Stub; nested exception is: 
	java.lang.ClassNotFoundException: client.Client_Stub
	at sun.rmi.server.Util.createStub(Unknown Source)
	at sun.rmi.server.Util.createProxy(Unknown Source)
	at sun.rmi.server.UnicastServerRef.exportObject(Unknown Source)
	at java.rmi.server.UnicastRemoteObject.exportObject(Unknown Source)
	at java.rmi.server.UnicastRemoteObject.exportObject(Unknown Source)
	at client.Client.<init>(Client.java:19)
	at client.Client.main(Client.java:28)
Caused by: java.lang.ClassNotFoundException: client.Client_Stub
	at java.net.URLClassLoader$1.run(Unknown Source)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.net.URLClassLoader.findClass(Unknown Source)
	at java.lang.ClassLoader.loadClass(Unknown Source)
	at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
	at java.lang.ClassLoader.loadClass(Unknown Source)
	at java.lang.ClassLoader.loadClassInternal(Unknown Source)
	at java.lang.Class.forName0(Native Method)
	at java.lang.Class.forName(Unknown Source)
	... 7 more
```

Hier der Code:

TestRemoteInterface.java

```
public interface TestRemoteInterface extends Remote {
	public int test(int t) throws RemoteException;
	public void addListener(client.Listener l) throws RemoteException;
}
```


```
package server;

//imports

public class TestRemoteInterfaceImpl implements TestRemoteInterface {
	Listener li;
	@Override
	public int test(int t) throws RemoteException {
		return t*2;
	}

	@Override
	public void addListener(Listener l) throws RemoteException {
		li = l;
		fireEvent();
	}
	
	public void fireEvent() throws RemoteException {
		li.update();
	}
}
```

Server.java

```
package server;

//imports...

public class Server {
	public Server() throws RemoteException {
		LocateRegistry.createRegistry(Registry.REGISTRY_PORT);
		TestRemoteInterface test = new TestRemoteInterfaceImpl();
		TestRemoteInterface stub = (TestRemoteInterface) UnicastRemoteObject.exportObject(test, 0);
		RemoteServer.setLog(System.out);
		Registry registry = LocateRegistry.getRegistry();
		registry.rebind("test", stub);
		System.out.println("test angemeldet");
	}

	public static void main(String[] args) throws Exception {
		new Server();
	}
}
```

Listener.java

```
package client;

//imports

public interface Listener extends Remote {
	public void update() throws RemoteException;
}
```

Client.java



```
package client;

//imports

public class Client implements Listener {
	public Client() throws Exception {
		Registry registry = LocateRegistry.getRegistry();
		TestRemoteInterface test = (TestRemoteInterface) registry
				.lookup("test");
		System.out.println(test.test(10));

		UnicastRemoteObject.exportObject(this);
		test.addListener(this);
	}

	public void update() throws RemoteException {
		System.out.println("update");
	}

	public static void main(String[] args) throws Exception {
		new Client();
	}
}
```


----------



## tuxedo (31. Jan 2008)

SebiB90 hat gesagt.:
			
		

> Hab mir das mal mit den Callbacks in dem Thread angeschaut und versuch zu implementieren als kleinen Test. Nur das Problem bei mir ist. Das wenn die Implementierung von dem Listener nicht von UnicastRemoteObject erbt, sondern mit der export Methode zu einem RemoteObject wird, dann diese Exceptions kommt. Nur keine Ahnung wieso:



Na und warum lässt du dann nicht von UnicastRemoteObject erben? Hab das auch immer so gemacht. Hab bis jetzt auch noch keine "Einschränkung" entdeckt.
Oder du nutzt gleich "SIMON" (würde mich dann über etwaige Bug-Reportings freuen) ;-)

- Alex


----------



## SebiB90 (31. Jan 2008)

alex0801 hat gesagt.:
			
		

> Na und warum lässt du dann nicht von UnicastRemoteObject erben?
> - Alex


weil 1. es in dem Thread anscheind auch ohne funktioniert.
und 2. der nachteil ist, dass die klasse von keiner anderen klasse mehr erben kann


----------



## tuxedo (31. Jan 2008)

Lässt du alles noch lokal laufen?
Die Meldung ist ja

>> Exception in thread "main" java.rmi.StubNotFoundException: Stub class not found: client.Client_Stub;
Gibts bei dir dieses Stub bei den .class-Files ?

Mit Java 6 hat sich da was geändert. Mit Java 5 und früher hat man noch nen RMI-Compiler gebraucht um so ein Stub zu erzeugen. Wie das mit Java 6 ist wieß ich nicht, jedenfalls geht's ohne RMI-Compiler  wenn ich alles von UnicastRemoteObject erben lasse. 

Ansonsten kann ich dir - ich muss mich schon wieder wiederholen - SIMON ans Herz legen. Da reicht es aus wenn die Remote-Objekte ein passendes Interface implementieren. Beispielcode (ist wirklich easy, ähnlich wie RMI) gibts hier: http://www.java-forum.org/de/topic62609_die-rmi-alternative-simon.html

- Alex


----------

