# Verständnishilfe - Netzwerk Spiel (Sockets)



## Raziell (12. Apr 2010)

Hallo zusammen,
bräuchte ein bisschen Unterstützung und Hilfe von euch bei der Programmierung
eines Netzwerk-Spiels (angelehnt an ein MMORPG) mit Hilfe von Sockets.

Meine Vorstellung:

1. Der Client sendet eine Tastendruck an den Server (Bsp.: l, r ,u ,o)
2. Der Server kennt natürlich die Position des Spielers und berechnet *ständig* die neue Position
3. Es soll eine Klasse Player geben, welche dann z.B. Attribute wie x und y Position des Spielers hat.
4. Der Server hat dann für jeden Spieler ein Objekt der Klasse Player z.B. in einem Vector gespeichert.
5. Der Server soll dann eine Klasse haben, welche z.B. alle 30ms die neuen x und y Position aller Spieler an alle Clients sendet.

Die Frage wäre jetzt, wie ich dieses Update der Spielerpositionen aufbauen sollte? 
Ich habe mir das ganze jetzt in etwa so vorgestellt:

Ich durchlaufe alle Spieler, hole mir deren ID sowie x und y Position und baue meinen 
String so zusammen. Der sendDates() Methode übergebe ich diesen String.
Die sendDates() Methode durchläuft alle verbundenen Sockets und sendet den String an 
jeden Client.


```
String playerCoordinates = "";
		for (int i = 0; i < players.size(); i++) {
			playerCoordinates += players.elementAt(i).getID() + ":" + players.elementAt(i).getXPos() + ":" + players.elementAt(i).getYPos() + ";";
		}
		server.sendDates(playerCoordinates);
```

Macht das ganze so Sinn oder wird der String die Leitung verstopfen? Dieser kann ja dann
durchaus, bei vielen Spielern auch etwas länger ausfallen 


Vielen Dank im Voraus

Grüße


----------



## Gast2 (12. Apr 2010)

Raziell hat gesagt.:


> Die Frage wäre jetzt, wie ich dieses Update der Spielerpositionen aufbauen sollte?


Asynchron - wirst um Threads nicht herum kommen

ein UDP-Port nimmt die Bewegungswünsche der Spieler entgegen ... der Berechnungs-Thread berechnet permanent die neue Position auf Grund der Wünsche ... die neuen Positionen sendet er via UDP wieder zurück

hand, mogel


----------



## Raziell (12. Apr 2010)

> ein UDP-Port nimmt die Bewegungswünsche der Spieler entgegen ... der Berechnungs-Thread berechnet permanent die neue Position auf Grund der Wünsche ... die neuen Positionen sendet er via UDP wieder zurück



Also derzeit habe ich serverseitig sowieso einen Thread für jede Socketverbindung,
welcher ständig auf eingehende Informationen horcht. Dazu wird serverseitig ein weiterer Thread
für jeden Spieler kommen, welcher permanent die Statusänderungen durchführt.

Ich arbeite mit TCP (Diskussionen darüber sind hier: http://www.java-forum.org/netzwerkprogrammierung/98523-verstaendinsfrage-spieleprogrammierung-datagrammsocket.html zu finden)

Die Frage ist ob es Sinn macht, den Updateprozess der Koordinaten vom Server an die Clients
so aufzubauen wie ich es geschildert habe?


Grüße


----------



## Raziell (5. Mai 2010)

Hallo zusammen,

ich habe ein paar Probleme und weiss nicht wie ich sie lösen soll.
Die Probleme beziehen sich auf folgendes:

Derzeigt bewegt sich der Spieler clientseitig und sendet die Änderung der Bewegungsrichtung
an den Server (bzw. ein Stopp, wenn er sich nicht mehr bewegt). 
Der Server kennt natürlich die Position des Spielers und berechnet ständig die neue Position. 

Das ganze läuft serverseitig in eta so ab:


```
package de.game.server.logic.player;

import de.game.globalObjects.GSocket;

public class Player_Update_Thread extends Thread {

	private GSocket playerSocket = null;
	private double pos = 1.8;
	private String direction = "s";
	private double x = 0;
	private double y = 0;

	public Player_Update_Thread(GSocket playerSocket) {
		this.playerSocket = playerSocket;
		x = playerSocket.getX();
		y = playerSocket.getY();
		start();
	}

	@Override
	public void run() {
		while (true) {
			while (!direction.equals("s")) {
				updatePosition();
			}
		}
	}

	public void updatePosition() {
		if (direction.equals("l")) {
			playerSocket.setX(x -= pos);
		}
		if (direction.equals("r")) {
			playerSocket.setX(x += pos);
		}
		if (direction.equals("u")) {
			playerSocket.setY(y -= pos);
		}
		if (direction.equals("d")) {
			playerSocket.setY(y += pos);
		}
		//		System.out.println("x Pos @ Server: " + playerSocket.getX());
		//		System.out.println("y Pos @ Server: " + playerSocket.getY());
		try {
			Thread.sleep(30);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

	public void setPosition(String content) {
		direction = content;
	}

}
```

Der Server sendet alle 30ms ein Positionsupdate an alle Clients. 

Das große Problem ist jetzt unter anderem, dass ich nicht weiss
wie ich mich serverseitig bewegen soll. Auf Clientseite sind es in etwa 1,8 Pixel oder was auch immer (mit Rectangle2D.Double errechnet) pro 30ms. 
Wenn ich jetzt serverseitig sage das er alle 30ms die Position in die jeweilige
Richtung ändern soll bis ein Stop kommt, dann haut das mit den Position auf clientseite nicht wirklich hin. 
Die Differenzen liegen dann immer so bei 5-11 Pixel. Das heisst ich müsste dann bei jedem
Serverupdate die Position auf Clientseite korrigieren.

Kann mir jmd. helfen bei dem Problem?

Danke und viel Grüße im Voraus


----------



## Gast2 (6. Mai 2010)

vergiss die Pixel ... rechne auf Serverseite mit Koordinaten ... der Client rechnet das dann wieder in Pixel um


----------



## Raziell (6. Mai 2010)

Hm also ich rechne ja beidseitig mit Koordinaten. Und zwar erbt mein Sprite von Rectangle2D.Double. Die Position wird dann mit setX() und setY() gesetzt.


----------



## Raziell (14. Mai 2010)

Hallo zusammen,
ich habe da noch ein Problem, für das ich einfach keine Lösung finde.
Und zwar geht es immernoch um das serverseitige berechnen der Koordinaten. Ich rechne ja auf Clientseite mit Rectangle2D.Double. Aber wie soll ich serverseitig rechnen? Wenn ich mich auf Client-Seite um etwa 1,8 bewege, um wieviel soll ich mich denn dann serverseitig bewegen?

Wenn ich einfach sage das er serverseitig auch ca. 1,8 innerhalb von 30ms bewegt, dann laggt
er über Internet ganz schön rum, da die Serverposition sich einfach zu sehr von der Clientposition
unterscheidet.

Das ganze Problem wird ja auch in meinem vierten Post beschrieben.

Danke im Voraus


----------



## Gast2 (14. Mai 2010)

Du bewegst Dich auf dem Server um X Units ... dann bewegst Du Dich auf dem Client um X Units ... somit ist die Bewegung erstmal gleich ... anschließend bildest Du die Bewegung auf die Darstellung ab ... Du bewegst Dich also um Y Pixel ... die Abbildung von X Units auf Y Pixel sollte eine einfache Skalierung sein


----------



## Raziell (14. Mai 2010)

Hm sorry ich verstehs leider nicht so ganz,
also ich dachte mir das es so abläuft, das ich z.B. auf clientseite links drücke und
mich dann sagen wir mal auf gut glück links bewege. Währenddessen berechnet der
Server, der vom Client die Bewegungsrichtung erhalten hat die neue Position. Der Server schickt dann
beispielsweise alle 30ms die neuen Positionen aller Clients an alle Clients. Die Clients korriegieren dann
ihre Positionen und passen Sie den Positionen vom Server an.
Ist mein Gedanke so richtig? Wie kriege ich denn das ganze so synchronisiert, dass die Positionen auf clientseite mit der berechneten Bewegung auf serverseite möglichst gleich sind. Ohne das die Spieler rumzucken.

Meinst du das ich mich auf clientseite nicht auf Gut Glück sondern erst auf Antwort vom Server bewegen soll?

Grüße


----------



## Empire Phoenix (15. Mai 2010)

schönrechnen ist das zauberwort: interpolation, prediction, google


----------



## Raziell (15. Mai 2010)

> schönrechnen ist das zauberwort: interpolation, prediction, google



Das mache ich schon, ich berechne auch die Positionen zwischen den erhaltenen Koordinaten.
Allerdings bringt mir das nicht sonderlich viel, wenn sich die Positionen auf clientseite
zu sehr von den Positionen auf serverseite unterscheiden.

Die Frage wäre ja auch noch offen:


> Wie kriege ich denn das ganze so synchronisiert, dass die Positionen auf clientseite mit der berechneten Bewegung auf serverseite möglichst gleich sind. Ohne das die Spieler rumzucken.
> 
> Meinst du das ich mich auf clientseite nicht auf Gut Glück sondern erst auf Antwort vom Server bewegen soll?



Gruß


----------



## Empire Phoenix (15. Mai 2010)

Also je nach update zahl ist es manchmal am einachsten gar nichts auffer clientseite zu tun ausser interpolation.


----------



## Raziell (16. Mai 2010)

Hi,
also wäre dein Vorschlag die Figur auf clientseite wirklich nur dann zu bewegen, wenn
ein Update vom Server kommt und den Rest zwischendurch zu interpolieren?



> Dein Client schickt den move-Befehl und aktualisiert sich dann shconmal selbst, *damit die Bewegung sofort spürbar ist.*


Das wird dann aber nicht gegeben sein oder?

Ich denke das größte Problem ist, dass ich dadurch dass sich Client- und Serverposition immer ein wenig unterscheiden, muss ich auf Clientseite die Positionen aller Spieler nach jedem Update vom Server
anpassen. Das belastet den Client und sorgt für ruckelige Bewegungen.

Also ich denke es gibt 2 Möglichkeiten das Problem zu lösen.

1. Ich muss irgendwie dafür sorgen das die Client- und Server-Position immer gleich ist. Wüsste allerdings nicht wie das klappen sollte.

2. Die Positionen aller Spieler auf clientseite nicht nach jedem Serverupdate sondern beispielsweise nur alle 100ms anpassen. Richtungsänderungen entgegennehmen und interpolieren.

Ich weiss garnicht ob ich mich überhaupt verständlich ausdrücke. Versteht eigtl. jmd. was ich meine? 


Grüße


----------



## Gast2 (16. Mai 2010)

Raziell hat gesagt.:


> 1. Ich muss irgendwie dafür sorgen das die Client- und Server-Position immer gleich ist. Wüsste allerdings nicht wie das klappen sollte.


nie ... da gibt es zuviele unbekante Variablen



> 2. Die Positionen aller Spieler auf clientseite nicht nach jedem Serverupdate sondern beispielsweise nur alle 100ms anpassen. Richtungsänderungen entgegennehmen und interpolieren


damit machst Du es nur schlimmer ... Du musst an Deiner Interpolation feilen


----------



## Raziell (16. Mai 2010)

Hat jmd. eventuell ein paar Tipps wie ich das Anpassen und Interpolieren der Positionen am besten lösen kann? Irgendwie fehlt mir der Ansatz. Ich habe auf clientseite Positionen und Bewegungsrichtungen aller Spieler, welche alle 30ms vom Server kommen.

Den Korrekturvorgang löse ich momentan so:


```
public void correctPlayerPositions(String content) {
		// Splitten der Speilerdaten vom Server ([0] Spieler-ID, [1,2] Spielerpositionen (x, y), [3] Bewegungsrichtungen)
		String[] players = content.split(";");
		for (int i = 0; i < players.length; i++) {
			String[] playerData = players[i].split("/");
			// Wenn ID = Eigene ID
			if (Integer.parseInt(playerData[0]) == id) {
				// Korrektur der eigenen Koordinaten
				gamePanel.receiveCoordinates(Double.parseDouble(playerData[1]), Double.parseDouble(playerData[2]));
				// Wenn nicht ID = Andere Spieler ID
			} else {
				for (int j = 0; j < gamePanel.getActors().size(); j++) {
					if (gamePanel.getActors().get(j) instanceof Actor) {
						if (playerData[0].equals(((Actor) gamePanel.getActors().get(j)).getId())) {
							// Korrektur der Koordinaten der anderen Spielfiguren
							gamePanel.getActors().get(j).setX(Double.parseDouble(playerData[1]));
							gamePanel.getActors().get(j).setY(Double.parseDouble(playerData[2]));
							// Setzen der neuen Bewegungsrichtung der anderen Spielfiguren (Interpolation) Die Bewegung der Spielfiguren findet immer statt
							if (playerData[3].equals("u")) {
								gamePanel.getActors().get(j).setVerticalSpeed(-speed);
								gamePanel.getActors().get(j).setHorizontalSpeed(0);
							} else if (playerData[3].equals("d")) {
								gamePanel.getActors().get(j).setVerticalSpeed(speed);
								gamePanel.getActors().get(j).setHorizontalSpeed(0);
							} else if (playerData[3].equals("r")) {
								gamePanel.getActors().get(j).setHorizontalSpeed(speed);
								gamePanel.getActors().get(j).setVerticalSpeed(0);
							} else if (playerData[3].equals("l")) {
								gamePanel.getActors().get(j).setHorizontalSpeed(-speed);
								gamePanel.getActors().get(j).setVerticalSpeed(0);
							} else {
								gamePanel.getActors().get(j).setHorizontalSpeed(0);
								gamePanel.getActors().get(j).setVerticalSpeed(0);
							}
						}
					}
				}
			}
		}
	}
```

Ich weiss das ganze ist unschön und klappt auch nicht besonders gut 

Ich hoffe mal jmd. kann mir helfen...

Grüße


----------



## Gast2 (17. Mai 2010)

Du musst die letzten X Positionen des Spielers merken und kannst dann über den Durchschnitt die nächste _mögliche_ Position berechnen


----------

