# Server Monitor [Quake III]



## Dit_ (30. Sep 2009)

Hallo!

Ich möchte ein einfaches Programm schreiben, das auf mein ET Server zugreift, aktuelle Spielerliste lädt und bestimmte admin funktionen durchführt (rcon einlogen, so wie in HLSW) usw... 

Ein Beispiel: Server Monitor ohne admin Funktion

Frage. Ich will ja nicht dass man mir eine Lösung gibt, aber vielleicht sagt ihr mir was ich dafür lesen, lernen bzw wissen muss. Wäre toll! Bin kein Profi und weiss nicht wo ich da anfangen muss.

:rtfm:

Gruss


----------



## Evil-Devil (30. Sep 2009)

Vielleicht solltest du auch sagen was du schon kannst. Zb. ob du überhaupt schon programmieren kannst oder direkt bei Null anfängst.


----------



## Dit_ (30. Sep 2009)

Programmieren kann ich, zumindest grundkenntnisse sind da.


----------



## Evil-Devil (30. Sep 2009)

Naja, dann musst du dich noch informieren worüber/wie man den ET Server ansprechen kann und noch wissen welche Daten er wie auslesen lässt. Wenn du das weißt, dann kannst du denke ich anfangen dich damit zu beschäftigen wie man das in Java realisiert.


----------



## Dit_ (1. Okt 2009)

```
function getstream($host, $port, $queryport)

    {
        // get the infostream from server
        $socket = fsockopen('udp://'. $host, $port, $errno, $errstr, 30);

        if ($socket === false)
        {
            echo "Error: $errno - $errstr<br>\n";
        }
        else
        {
            socket_set_timeout($socket, 3);
            $time_begin = $this->microtime_float();
            fwrite($socket, $this->write);
            $this->s_info = fread($socket, $this->maxlen);
            $time_end = $this->microtime_float();
        }
        fclose($socket);
```

dies ist ein code auszug aus einem php script. wie es aussieht hat hier Socket 5 Parameter :/ 
normalerweise hat der game server ja nur IP und port oder ?


----------



## Opnox (1. Okt 2009)

Guck halt im PHP Manual nach was fsockopen alles braucht und was die Parameter bedeuten.
PHP: fsockopen - Manual


----------



## Evil-Devil (1. Okt 2009)

Mal davon abgesehen sind die Bezeichner der übergebenden Variablen doch sehr selbsterklärend.
Host, Port, Fehlernummer, Fehlertext und das letzte entsprechend die Timeout Zeit. Mit ein wenig nachdenken kommt man da sogar ohne Manual drauf.


----------



## Dit_ (1. Okt 2009)

```
public static String sendRcon(String address, int port, String password, String command) throws SocketException, IOException {
        byte[] header = { (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff };
        byte[] body = ("rcon " + password + " " + command).getBytes();
        byte[] buf = new byte[1024];
        System.arraycopy(header, 0, buf, 0, header.length);
        System.arraycopy(body, 0, buf, header.length, body.length);
        DatagramPacket dp = new DatagramPacket(buf, buf.length, new InetSocketAddress(address, port));
        DatagramSocket ds = new DatagramSocket();
        ds.send(dp);
        ds.receive(dp);
        String data = new String(dp.getData());       
        return data;
    }
```




Habe hier im Forum das gefunden.
Kann mir jemand sagen was dies bedeutet


```
byte[] header = { (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff };
        byte[] body = ("rcon " + password + " " + command).getBytes();
        byte[] buf = new byte[1024];
        System.arraycopy(header, 0, buf, 0, header.length);
        System.arraycopy(body, 0, buf, header.length, body.length);
```

also ich sehe schon was da gemacht wird und verstehe auch wie. Aber wozu?

also wenn ich das so starte und als command "status" sende dann bekomme ich folgendes:


```
ÿÿÿprint
map: fun_beach_final
num score ping name            lastmsg address               qport rate
--- ----- ---- --------------- ------- --------------------- ----- -----
  0     0   42 ^9[AwB]^1Dit^0(Riflenoob)     50 92.75.148.90:27964    [][][][][][][][][][][].....sehr viele davon
```

Hier ist mein code:


```
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;


public class Connect {

	/** Rcon Passwort, erlaubt die adminbefehle auszuführen */
	private final String RCON = "43434bla";
	/** ServerIP */
	private String serverIP;
	/** ServerPort */
	private int port;
	/** Als Admin oder nicht */
	private boolean mitRcon;
	/** Anfrage Befehl */
	private byte[] anfrageWort;

	/**
	 * Konstruktor
	 * 
	 * @param serverIP
	 * @param port
	 * @param mitRcon
	 */
	public Connect(String serverIP, int port, boolean mitRcon) {
		this.serverIP = serverIP;
		this.port = port;
		this.mitRcon = mitRcon;
	}

	public void sendeAnfrage(String anfrage) throws IOException {

		setAnfrageWort(anfrage);

		DatagramPacket anfragePacket = new DatagramPacket(anfrageWort,
				anfrageWort.length, new InetSocketAddress(serverIP, port));
		DatagramSocket ds = null;
		
		ds = new DatagramSocket();
		ds.send(anfragePacket);
		ds.receive(anfragePacket);
		
		String data = new String(anfragePacket.getData());
		System.out.print(data);

		
		
    	data = data.substring(29);
    	String[] tokens = data.split("\" default:");
    	data=tokens[0];
    	System.out.print(data);
    	


	}

	private void setAnfrageWort(String anfrage) {
		byte[] header = { (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff };
		anfrageWort = new byte[1024];
		byte[] body;

		if (mitRcon) {
			//System.out.println(("rcon " + RCON + " " + anfrage));
			body = ("rcon " + RCON + " " + anfrage).getBytes();
		} else {
			body = (anfrage).getBytes();
		}
		System.arraycopy(header, 0, anfrageWort, 0, header.length);
		System.arraycopy(body, 0, anfrageWort, header.length, body.length);
	}

}
```
Ich bekomme nicht die vollständige Liste der SPieler .
Kann mir jem sagen warum. Und was ist die bestimmung von 

```
byte[] header = { (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff }; ?
```


----------



## ice-breaker (1. Okt 2009)

sicher, dass der Code dafür ist?
Weil das rcon-System wird von Cs1.6/CSS genutzt, würde mich stark wundern, wenn Quake auch darauf setzen würde.


----------



## Dit_ (2. Okt 2009)

ja es sollte mit und ohne rcon gehen, mit rcon kann man halt noch bestimmte befehle ausführen wie kick oder say usw... bei mir geht es nur mit rcon :/. und die liste der spieler wird auch nicht vollständig geladen. nur der erste spieler wird aufgelistet


----------



## Gast2 (2. Okt 2009)

hast Du schon eine Protokollbeschreibung wie man mit dem Quake-Server kommuniziert? ... es ist durchaus möglich das das Paket vom Netzwerk zerstückelt wird (weil es zu groß ist) ... oder das jeder Spieler einzeln geschickt wird


----------



## lumo (2. Okt 2009)

hab das ganze schon implementiert gefunden.
und zwar nennt sich das queried
aber ich warn dich mal vor, das ganze ist so, wie es veröffentlicht wurde nur mit vorsicht zu genießen.

hab das ganze paket überarbeitet (allerdings für battlefield 2) und stabil hingebracht.
mein bf2 server monitor ist stabil mit statusänderung etc...

queried unterstützt


> AA, BF1942, BF2142, BF2, BFV, D3, ET, HL, NEX, NWN, Q4, SOF2Q3, Source, UT2, UT, WSW


wobei die details der unterstützten funktionen etwas schwanken (in bf2 fehlten einige statistisch interessanten features - die ich dann erweitert hab)

-> lad dir queried und sieh es dir an, dann kommst sicher zu dem, was du machen willst

PS: sieh dir auch noch xsocket an, damit hab ich dann den server monitor implementiert
PPS: bei problemen kann ich weiterhelfen  - oder wen es interessiert kann ich auch einen screenshot von meinem monitor online stellen (ist ein echtzeitmonitor, der als http server für den client die daten als html&css&ajax zur verfügung stellt, mit datenbankanbindung für komplettes statistiktracking :toll


----------



## Dit_ (2. Okt 2009)

ok follgendes


```
private void frageSpielerListe() throws IOException {
		String anfrage = "status";
		byte[] anfrageWort = setAnfrageWort(anfrage);
		byte[] antwort = new byte[1024];
		DatagramPacket anfragePacket = new DatagramPacket(anfrageWort, anfrageWort.length, new InetSocketAddress(serverIP, port));
		socket = new DatagramSocket();
		socket.setSoTimeout(1000);
		DatagramPacket bekommePacket = new DatagramPacket(antwort, antwort.length);
		socket.send(anfragePacket);

		byte[] antwortPaket;

		socket.receive(bekommePacket);  // <<<<< EIN MAL
		
		antwortPaket = bekommePacket.getData();

		System.out.println((new String(antwortPaket, 0, bekommePacket
				.getLength())));

		socket.close();
	}
```

Ausgabe: Wenn 	"socket.receive(bekommePacket); " nur einmal aufgerufen wird

```
ÿÿÿÿprint
map: marketgarden_et_r2
num score ping name            lastmsg address               qport rate
--- ----- ---- --------------- ------- --------------------- ----- -----
  6     0   80 ^4MAX(rus)            0 90.191.195.141:55553  12246
```


Ausgabe: Wenn 	"socket.receive(bekommePacket); " ZWEI (!) einmal aufgerufen wird

```
ÿÿÿÿprint
 25000
  7     0   50 ^9[AwB]^0L^7ogiK^0-      0 92.200.95.108:27960   32516 25000
  8  3038   61 taschenla*mp3         0 84.160.88.167:27960   50050 40000
  9 23022  100 ^1Miha                0 92.196.115.44:27960   54347 25000
 10
```

Heisst das jetzt Packet ist zu gross? Beim Zweiten Mal gibt der Server die daten die nicht reingepasst haben oder ? Wenn das so ist, wie kann das problem lösen ?


----------



## Dit_ (2. Okt 2009)

@LUMO

Hi
was meinst du mit "queried" kann nichts dazu im Google finden.


----------



## lumo (2. Okt 2009)

wow anscheinend ist das teil SO alt dass es schon nicht mal mehr in google ist :lol:

aber auf *sourceforge* gibts das projekt noch - allerdings das letzte update gabs 2003... also nehme ich mal an, dass die bugs in die ich gelaufen bin noch immer rumliegen...


----------



## Dit_ (3. Okt 2009)

Die Frage ist immer noch aktuell 

Weiss keiner wieso wenn ich "socket.receive(bekommePacket); " ZWEI mal aufrufe, bekomme ich mehr infos als ausgabe.
bzw wie kann ich die ganze Tabelle bekommen. Muss ich mein "bekommePaket" irgendwie aufteilen?


----------



## lumo (3. Okt 2009)

sieht auf den ersten blick so aus, als ob du einen receivebuffer angegeben hast, der eine bestimmte größe hat -> bekommst du das ergebnis geteilt


----------



## Dit_ (4. Okt 2009)

ok aber das geht ja nicht anders ich muss ja DatagramPacket als  
	
	
	
	





```
new DatagramPacket(byte[] buf, int length);
```
 erstellen.

Oder soll ich 

```
new DatagramPacket(byte[] buf, int offset, int length);
```
 versuchen?

Was ist offset? Verstehe nicht ganz was in der Klassbeschreibung mit offset gemeint wird :bahnhof:


----------



## ice-breaker (4. Okt 2009)

Der Offset ist quasi die erste Stelle im Array, die eigentlichen Daten enthält, die in das Packet rein sollen, denn eventuell hast du in deinem Array vorne dran noch Dinge die du nicht verschicken willst, damit kannst du sie einfach überspringen ohne ein neues Array erstellen zu müssen.


----------



## Dit_ (16. Okt 2009)

Nächste Frage 

Es funktioniert soweit sehr gut. Jetzt möchte ich die Einstellungen des Programms speichern.
Gespeichert soll:

- IP adresse
- Server name
- Passwort

und einstellungen der Fensteransicht...

Wie ist es am besten zu lösen? 
Eine oder mehrere Dateien speichern oder mit einer Datenbank ?

Gruss


----------



## lumo (16. Okt 2009)

eine datei, datenbank brauchts dazu nicht...

Das ist, was du suchst 
JAVA Properties Wikipedia
Properties (Java 2 Platform SE 5.0)


----------

