---------------------------------------------------------------
22.07.2013 / Teil 1
Grundlagen: Mehrere Clienten senden Positionsdaten an den Server
---------------------------------------------------------------
Achtung: Neue Erweiterung weiter unten in Teil 2
(Server sendet Positionsdaten zurück an Clienten)
22.07.2013 / Teil 1
Grundlagen: Mehrere Clienten senden Positionsdaten an den Server
---------------------------------------------------------------
Achtung: Neue Erweiterung weiter unten in Teil 2
(Server sendet Positionsdaten zurück an Clienten)
Thema mit dem Beitrag welcher mich zu diesem Tutorial veranlasste: gm-d.de/wbb/index.php/Thread/21042/?highlight=
Um es für den Anfang einfach zu halten machen wir zwei Programme.
Den Server welcher in diesem Tutorial nur Daten empfängt,
und den Client welcher in diesem Tutorial nur Daten versendet.
SERVER
1. Als erstes erstellen wir ein neues Projekt und nennen es Server.
Unter Ressources > Define Constants oder mit Shft+Ctrl+N legen wir ein par Konstanten fest.
CMD_PING = 0;
CMD_POS = 1;
INFO_CREDIT = "Netzwerk Tutorial von 'Balls of Steel'"
Später mehr hierzu.
Gehen wir eins nach dem anderen durch, von oben nach unten
2. Sprites
Wir benötigen ein Bild, wie es aussieht ist für das Tutorial unwichtig. Am besten etwas kleines 32x32 Pixel oder so.
Wir geben ihm einen Namen im Tutorial nennen wir unsere Kugel mal spr_BallofSteel und setzen Origin auf Center.
3. Fonts
Erstellt einfach eine Schriftart wie sie euch beliebt, am besten 10 oder 12 Pixel groß und nennt sie font_1.
4. Objects
Wir erstellen 2 Objekte.
Das Objekt obj_Server welches die Daten empfängt und das Objekt obj_BallofSteel welches auf die empfangenen Daten reagiert.
Zum Objekt obj_Server
Sprite: <no sprite>
Events: Create, Networking, Draw
Im Create Event schreiben wir folgenden Code:
GML-Quellcode
Was passiert?
Wir öffnen mit network_create_server den Port 6510 und lassen maximal 16 Spieler zu.
Da wir unterscheiden wollen von wem die Daten kommen brauchen wir eine Liste in der die Sockel gespeichert werden und eine Map in der die dazugehörigen IDs der Instanzen mit den Sockeln verbunden werden.
(Für jeden Client der sich beim Server anmeldet werden wir ein obj_BallofSteel erstellen was auf seine eingaben regiert)
Im Networking Event schreiben wir folgenden Code:
GML-Quellcode
- /// Netzwerkempfang
- var eventid = ds_map_find_value(async_load, "id");
- if(server == eventid) // wenn die Sockel-ID zum Server gehört kommt eine neue Verbindung rein, oder wird unterbrochen
- { var t = ds_map_find_value(async_load, "type");
- var sock = ds_map_find_value(async_load, "socket");
- var ip = ds_map_find_value(async_load, "ip");
- if(t == network_type_connect) // FALLS EINE VERBINDUNG HERGESTELLT WIRD
- { if!(ds_map_exists(map_spieler, sock)) // Falls der Spieler noch nicht existiert --> erstelle ihn
- { var inst = instance_create(room_width / 2, room_height / 2, obj_BallofSteel); // Setze die Spielfigur auf das Spielfeld
- ds_list_add (list_spieler, sock); // Setze den Spieler auf die Verbindungsliste der Sockel
- ds_map_add (map_spieler, sock, inst); // Verknüpfe in der Spielerliste den Client-Sockel mit der Client-Objekt-Instanz
- inst.sock = sock;
- inst.ip = ip;
- }
- }
- else // FALLS EINE VERBINDUNG ABGEBROCHEN WIRD
- { if(ds_map_exists(map_spieler, sock)) // Falls der Spieler existiert --> lösche ihn
- { inst = ds_map_find_value(map_spieler, sock); // Suche die Client-Objekt-Instanz
- var index = ds_list_find_index(list_spieler, sock); // Suche den Index des Sockels aus der Sockelliste
- ds_list_delete(list_spieler, index); // Lösche den Sockel aus der Sockelliste
- ds_map_delete(map_spieler, sock); // Lösche den Spieler von der Liste
- with(inst)
- { instance_destroy(); // Lösche die Spielfigur vom Spielfeld
- }
- }
- }
- }
- else // Alle anderen Sockel sind von Spielern und wir bekommen Daten von ihnen
- { var buff = ds_map_find_value(async_load, "buffer"); // Erstelle den Puffer in den die Daten kommen
- var cmd = buffer_read(buff, buffer_s16); // Auslesen des Befehls
- var sock = ds_map_find_value(async_load, "id"); // Auslesen der Sockel-ID des Clients als Schlüssel
- var inst = ds_map_find_value(map_spieler, sock); // Auslesen der Instanzenzuordung des Clients
- if(cmd == CMD_PING) // Wir wurden nur angepingt mehr nicht
- {
- }
- else if(cmd == CMD_POS) // Es kommen Koordinaten rein
- { inst.x = buffer_read(buff, buffer_s16 ); // X an Instanz übergeben
- inst.y = buffer_read(buff, buffer_s16 ); // Y an Instanz übergeben
- }
- }
Was passiert?
Im oberen Teil überprüften wir mit if(t == network_type_connect) ob eine Verbindung aufgebaut wurde.
Wenn ja dann erstellen wir für diese Verbindung ein obj_BallofSteel und tragen die Verbindung in die Liste und die Map ein.
Wenn eine Verbindung abgebrochen wurde machen wir logischerweise genau das Gegenteil. Wir entfernen die Verbindung aus der Liste und der Map und löschen die zugehörige Instanz von obj_BallofSteel.
Im unteren teil reagieren wir auf alle Datenpackete welche nichts mit einer neuen Verbindung oder einem Verbindungsabbruch zu tun haben.
Es gibt in diesem Tutorial 2 Grundlagen beim handel mit Daten. Erstens ermitteln wir über die ID und den Sockel die zugehörige Instanzen ID. Zweitens gibt der erste Datensatz den der Client mit dem Packet sendet aufschluss über dessen Verwendungszweck. Hier CMD_PING und CMD_POS.
Diese stellen Konstanten dar zur leichteren Verständlichkeit falls es mal eine erweiterte Kommunikation wird.
Im Draw Event schreiben wir folgenden Code:
Was passiert?
Wir wählen als Schriftart die font_1 aus welche wir vorher erstellt haben und setzten die Schriftfarbe auf Schwarz.
Dan ein wenig unnötiger Infotext und am Schluss geben wir mit ds_list_size(list_spieler) an wie viele Spieler mit dem Server verbunden sind.
Zum Objekt obj_BallofSteel
Sprite: spr_BallofSteel
Events: Draw
Eigentlich wird das Objekt obj_BallofSteel nur benutzt und ist ein Opfer der Umstände den es wird in der Server.exe nur durch die empfangenen Daten herumgeschubst und nicht mehr.
Im Draw Event schreiben wir folgenden Code:
Es erhält zur verständlichkeit noch ein kleine Aufgabe, nämlich das es Informationen über seinen Client zeichnet und an welcher Position es sich befindet.
5. Rooms
Für unser Tutorial reicht uns ein Raum... wir könnten ihn Raum_1 nennen! Aber eigentlich ... ist das auch egal.
Wir brauchen den Raum nicht verändern, lediglich vielleicht die Hintergrundfarbe anpassen um die Server.exe gleich zu erkennen.
In den Raum setzten wir an eine beliebige Stelle das Objekt obj_Server, damit dies mit dem starten des Raums erstellt wird.
Ja und somit wär die Server.exe auch schon fertig.
CLIENT
1. Nun erstes erstellen wir ein neues Projekt und nennen es Client.
Unter Ressources > Define Constants oder mit Shft+Ctrl+N legen wir die selben Konstanten wie im Server fest.
CMD_PING = 0;
CMD_POS = 1;
INFO_CREDIT = "Netzwerk Tutorial von 'Balls of Steel'"
Später mehr hierzu.
Gehen wir eins nach dem anderen durch, von oben nach unten
2. Sprites
Wir benötigen wieder ein Bild, am besten wir importieren das von vorhin, oder erstellen einfach ein neues mit gleicher Größe.
3. Scripts
Wir erstellen ein neues Script mit folgendem Code:
GML-Quellcode
- /// Stubs mal den Server an um zu sehen ob er lust hat was zu empfangen
- { buffer_seek(buffer, buffer_seek_start, 0); // Setze den Datenpuffer auf 0 zurück
- buffer_write(buffer, buffer_s16, CMD_PING); // Schreibe in den Puffer die Art der übertragung
- network_send_packet(client, buffer, buffer_tell(buffer)); // Verschicke den Puffer
- }
Was passiert?
Wann immer wir dieses Script ausführen wird eine 0 zum Server gesendet. Warum? buffer_write(buffer, buffer_s16, CMD_PING); ihr errinnert euch, CMD_PING ist eine Konstante und diese beinhaltet die Zahl 0.
Für was ist das gut, naja es könnte benutzt werden um dem Server ein Lebenszeichen zu geben, oder um vom Server ein Lebenszeichen anzufordern.
Vielleicht gehen wir da mal in einer Erweiterung des Tutorials genauer drauf ein.
Wir duplizieren das eben erstellte Script und ändern den Code wie folgt ab:
GML-Quellcode
- /// Melde dem Server die Position des Objekts
- /// argument0 = x
- /// argument1 = y
- { buffer_seek(obj_Client.buffer, buffer_seek_start, 0);
- buffer_write(obj_Client.buffer, buffer_s16, CMD_POS);
- buffer_write(obj_Client.buffer, buffer_s16, argument0);
- buffer_write(obj_Client.buffer, buffer_s16, argument1);
- network_send_packet(obj_Client.client, obj_Client.buffer, buffer_tell(obj_Client.buffer));
- }
Was passiert?
Das ist jetzt schon interessanter. Dieses Script werden wir benutzen um die Koordinaten X und Y an den Server zu senden. Da wir vorhaben dieses Script mehrmals von verschiedenen Instanzen aus zu benutzen müssen wir vor jedem Puffer auf das Objekt obj_Client verweisen welches die Puffervariable enthalten wird. Natürlich auch so bei der Variablen Client welche die Adresse des Servers enthält.
Dieses Packet enthält folglich drei Informationen, CMD_POS also die Konstante welche dem Server mitteilt hier kommen Koordinaten, und die eigentlichen Koordinaten X und Y.
3. Fonts
Die benötigte Schriftart importieren wir wieder aus dem Server Projekt welches wir zuvor erstellt haben als font_1.
4. Objects
Wir erstellen wieder 2 Objekte.
Das Objekt obj_Client welches die den Datenpuffer zum Senden beinhaltet und das Objekt obj_BallofSteel welches jetzt auf die Tastendrücke des Benutzers reagiert und seine Positionsdaten mithilfe des Datenpuffers des Objekt obj_Client an den Server sendet.
Zum Objekt obj_Client
Sprite: <no sprite>
Events: Create, Draw
Im Create Event schreiben wir folgenden Code:
GML-Quellcode
- /// Erstelle eine Verbindung zum Server
- client = network_create_socket( network_socket_tcp );
- network_connect( client, "127.0.0.1", 6510 ); // In diesem Fall verbinden wir nur zum LocalHost also zu uns selbst
- buffer = buffer_create( 256, buffer_grow, 1); // Datenpuffer für Netzwerkübertragungen
- Send_Ping();
Was passiert?
Wir erstellen eine neue TCP-Verbindung und verbinden zum Server welcher ja am Port 6510 horcht und unter der IP 127.0.0.1 zu erreichen ist.
Wir erstellen einen Datenpuffer für die Netzwerkübertragungen.
Wir senden Testdaten an den Server. Wird vielleicht in weiteren Tutorials noch genauer ausgebaut.
Eigentlich reicht das schon, aber wir lassen noch ein par Informationen vom Draw event zeichnen, hier der Code:
Nun zum Objekt obj_BallofSteel
Sprite: spr_BallofSteel
Events: Step, Keyboard <Left>, Keyboard <Up>, Keyboard <Right>, Keyboard <Down>, Outside Room
Im Step Event schreiben wir folgenden Code:
Hier lassen wir das Script zum Senden der Positionsdaten ausführen.
Es sendet mit jedem Frame seine Position an den Server.
Im Keyboard <Left> Event schreiben wir folgenden Code:
Solange die Taste <PFEIL LINKS> gedrückt wird bewegt sich das Objekt obj_BallofSteel jeden Frame um 3 Pixel nach links.
Im Keyboard <Up> Event schreiben wir folgenden Code:
Solange die Taste <PFEIL OBEN> gedrückt wird bewegt sich das Objekt obj_BallofSteel jeden Frame um 3 Pixel nach oben.
Im Keyboard <Right> Event schreiben wir folgenden Code:
Solange die Taste <PFEIL RECHTS> gedrückt wird bewegt sich das Objekt obj_BallofSteel jeden Frame um 3 Pixel nach rechts.
Im Keyboard <Down> Event schreiben wir folgenden Code:
Solange die Taste <PFEIL UNTEN> gedrückt wird bewegt sich das Objekt obj_BallofSteel jeden Frame um 3 Pixel nach unten.
Im Event Outside Room schreiben wir folgenden Code:
Wird ja schon im Code erklärt.
5. Rooms
Für unser Tutorial reicht uns ein Raum... wir könnten ihn Raum_1 nennen! Aber eigentlich ... ist das auch egal.
Wir brauchen den Raum nicht verändern, lediglich vielleicht die Hintergrundfarbe anders als in der Server.exe setzen.
In den Raum setzten wir an eine beliebige Stelle das Objekt obj_Client, und in die Mitte das Objekt obj_BallofSteel damit diese mit dem starten des Raums erstellt werden.
Ja und somit wär die Client.exe auch fertig.
Als Dateianhänge findet ihr die beiden Projekte
P.S. Ich hab den Configs Ordner in den Projekten gelöscht, da der ja fast 4MB groß ist. Könnt ihr ja wenn ihr ein neues Projekt erstellt dort rauskopieren.
Ich weis es ist bestimmt nicht das beste Tutorial, doch hab ich mich bemüht alles so gut wie möglich zu erklären.
---------------------------------------------------------------
Edit: 23.6.2014 / Teil 2
Erweiterung: Server sendet alle Positionen der Spieler zu den Clients zurück
---------------------------------------------------------------
Edit: 23.6.2014 / Teil 2
Erweiterung: Server sendet alle Positionen der Spieler zu den Clients zurück
---------------------------------------------------------------
Nun kommt hier mal die erste Erweiterung des Tutorials in welcher der Server die empfangenen Daten auch zu den Clients zurücksendet.
SERVER
1. Als erstes bearbeiten wir das Programm des Servers.
Unter Ressources > Define Constants oder mit Shft+Ctrl+N legen wir ein par neue Konstanten fest.
CMD_RECALL = 2; //Konstante mit welcher wir dem Client mitteilen das jetzt Daten zurückgesendet werden.
CMD_DEL = 3; // Konstante mit welcher wir dem Client mitteilen das folgende Spieler nicht mehr existieren.
2. Objects
Beim Server hat sich an sich nicht viel geändert, wir müssen nur das obj_Server dazu bringen Daten zu sammeln und zu versenden.
Das Create Event ergänzen wir um folgenden Code:
Was passiert?
Wir erstellen einen Puffer in welchen wir die zu versendenden Daten legen werden.
Notiz: Genau das gleiche wie im Create Event von obj_Client des Client Programms.
Wir legen ein neues Step Event an und fügen folgenden Code ein:
GML-Quellcode
- /// Sende an die Clienten
- var_size = ds_list_size(list_spieler);
- if(var_size > 0) // 1*
- { for(i = 0; i < var_size; i++) // 2*
- { var_inst = ds_map_find_value(map_spieler, ds_list_find_value(list_spieler, i)); // 3*
- buffer_seek(buffer, buffer_seek_start, 0); // 4*
- buffer_write(buffer, buffer_s16, CMD_RECALL); // 5*
- buffer_write(buffer, buffer_s16, var_size -1); // 6*
- for(ii = 0; ii < var_size; ii++) // 7*
- { var_id = ds_map_find_value(map_spieler, ds_list_find_value(list_spieler, ii)); // 3*
- if(var_inst != var_id) // 8*
- { buffer_write(buffer, buffer_f32, var_id); // 9*
- buffer_write(buffer, buffer_s16, var_id.x); // 10*
- buffer_write(buffer, buffer_s16, var_id.y); // 11*
- }
- }
- network_send_packet(var_inst.sock, buffer, buffer_tell(buffer)); // 12*
- }
- }
Was passiert?
1* Wir überprüfen ob Spieler in die Spierliste eingetragen sind.
2* Falls ja starten wir eine for-Schleife mit welcher wir die Daten an Jeden Spieler schicken.
3* Wir holen uns die Instanz-ID's mit hilfe der Spielerliste aus der Spielermap
4* Wir beginnen mit dem beschreiben des Puffers
5* Mit CMD_RECALL teilen wir dem Client mit das jetzt Spielerdaten an ihn zurückkommen
6* Wir teilen dem Client mit wie viele Daten wir senden, -1 da wir uns selbst ja nicht mitsenden
7* Mit dieser for-Schleife sammeln wir die ID's und Postionen der einzelnen Spieler um sie an den die Einzelnen Spieler zu senden
8* Die Daten der eigenen Instanz schicken wir natürlich nicht an den Clienten
9* Schreibe die ID des Spielers(obj_BallofSteel) in den Puffer
10* Schreibe die X Position des Spielers in den Puffer
11* schreibe die Y Position des Spielers in den Puffer
12* Verschicke die Daten an den jeweiligen Clienten
Wir müssen das Networking Event ergänzen, hier zwischen Start Erweiterung und Ende Erweiterung:
GML-Quellcode
- ...
- else // FALLS EINE VERBINDUNG ABGEBROCHEN WIRD
- { if(ds_map_exists(map_spieler, sock)) // Falls der Spieler existiert --> lösche ihn
- { inst = ds_map_find_value(map_spieler, sock); // Suche die Client-Objekt-Instanz
- var index = ds_list_find_index(list_spieler, sock); // Suche den Index des Sockels aus der Sockelliste
- // Start Erweiterung: sende CMD_DEL an Clienten
- var_size = ds_list_size(list_spieler);
- for(i = 0; i < var_size; i++) // 1*
- { var_inst = ds_map_find_value(map_spieler, ds_list_find_value(list_spieler, i)); // 2*
- if(var_inst != inst) // 3*
- { buffer_seek(buffer, buffer_seek_start, 0); // 4*
- buffer_write(buffer, buffer_s16, CMD_DEL); // 5*
- buffer_write(buffer, buffer_f32, inst); // 6*
- network_send_packet(var_inst.sock, buffer, buffer_tell(buffer)); // 7*
- }
- }
- // Ende Erweiterung
- ds_list_delete(list_spieler, index); // Lösche den Sockel aus der Sockelliste
- ds_map_delete(map_spieler, sock); // Lösche den Spieler von der Liste
- with(inst)
- { instance_destroy(); // Lösche die Spielfigur vom Spielfeld
- }
- }
- }
- ...
Was passiert?
Bis jetzt wurde bei Verbindungsabbruch nur das obj_BallofSteel auf dem Server gelöscht und gut wars.
Jetzt aber müssen wir jedem Clients mitteilen das sie auch die entsprechende Instanz löschen müssen.
1* Wir starten wieder eine for-Schleife um alle Spieler zu erreichen.
2* Wir holen uns wieder die ID jeder Instanz.
3* Natürlich schicken wir die Daten nicht an den Clienten welcher gerade beendet wurde.
4* Wir beginnen mit dem beschreiben des Puffers
5* Mit CMD_DEL teilen wir dem Client mit das jetzt der Befehl zum löschen eines Objektes kommt.
6* Wir teilen den Clienten mit welche Instanz sie löschen müssen.
7* Wir verschicken das ganze an die einzelnen Clienten.
Wir erweitern das Draw Event um zu sehen welche Instanzen gesendet werden (optional):
Was passiert?
Wir lassen die Server-ID's der Objekte obj_BallofSteel zeichnen damit wir sehen was wirklich gesendet wird.
Und das waren auch schon alle Veränderungen welche am Serverprogramm notwendig sind.
CLIENT
1. Beim Client müssen wir die selben Konstanzen hinzufügen.
Unter Ressources > Define Constants oder mit Shft+Ctrl+N legen wir ein par neue Konstanten fest.
CMD_RECALL = 2; //Konstante mit welcher wir dem Client mitteilen das jetzt Daten zurückgesendet werden.
CMD_DEL = 3; // Konstante mit welcher wir dem Client mitteilen das folgende Spieler nicht mehr existieren.
2. Sprites
Wir benötigen ein neues Sprite um den eigenen Clienten von den anderen zu unterscheiden.
Hierzu duplizieren wir einfach das Sprite spr_BallofSteel und geben ihn den neuen Namen spr_BallofSteel_Recall.
Nun bearbeiten wir das neue Sprite noch und färben den roten Punkt in der mitte grün ein.
3. Objects
Unter Objects legeb wir ein neues Objekt an mit dem Namen obj_BallofSteel_Recall und weisen ihm das gerade erstelle Sprite zu.
Zudem setzen wir die Depth auf 10 damit es immer schön unter dem roten Ball bleibt.
Kümmern wir uns nun um das Objekt obj_Client.
Im Create Event müssen wir eine Liste, eine Map und einen Server anlegen:
Nun zum aufwendigsten teil, dem Networking Event. Dieses müssen wir neu erstellen und mit folgendem Code füttern:
GML-Quellcode
- /// Netzwerkempfang
- var eventid = ds_map_find_value(async_load, "id");
- if(server != eventid) // 1*
- { var buff = ds_map_find_value(async_load, "buffer"); // 2*
- var cmd = buffer_read(buff, buffer_s16); // 3*
- var sock = ds_map_find_value(async_load, "id"); // 4*
- if(cmd == CMD_RECALL) // 5*
- { var_recall = buffer_read(buff, buffer_s16); // 6*
- for(i = 0; i < var_recall; i++) // 7*
- { var_id = buffer_read(buff, buffer_f32); // 8*
- var_id_obj = ds_map_find_value(map_recall, var_id); // 9*
- if(var_id_obj > 100000) // 10*
- { var_id_obj.x = buffer_read(buff, buffer_s16); // 11*
- var_id_obj.y = buffer_read(buff, buffer_s16); // 12*
- }
- else // 13*
- { var_id_obj = instance_create(10, 10, obj_BallofSteel_Recall); // 14*
- ds_list_add(list_recall, var_id); // 15*
- ds_map_add(map_recall, var_id, var_id_obj); // 16*
- var_id_obj.x = buffer_read(buff, buffer_s16); // 17*
- var_id_obj.y = buffer_read(buff, buffer_s16); // 18*
- }
- }
- }
- if(cmd == CMD_DEL) // 19*
- { var_id = buffer_read(buff, buffer_f32); // 20*
- var_id_obj = ds_map_find_value(map_recall, var_id); // 21*
- if(var_id_obj > 100000) // 22*
- { with(var_id_obj)
- instance_destroy(); // 23*
- }
- }
- }
Was passiert?
1* Falls dies eine bestehende Verbindung ist tue nachfolgendes
2* Es werden die Daten aus dem Puffer geholt
3* Lies den ersten Befehl aus um zu wissen wie weiter vorgegangen wird
4* Schau nach über welchen Sockel das ganze läuft
5* Falls dies ein CMD_RECALL befehl ist und somit Spielerpositionen kommen tue folgendes
6* Lies aus wie viele Daten kommen
7* Starte eine for-Schleife um die Daten auszuwerten
8* Lies die Instanz-ID des Server-Objektes aus
9* Suche mithilfe der Instanz-ID des Servers die Instanz-ID des Clienten
10* Falls die Instanz-ID schon bekannt ist tue folgendes
11* Übergebe der Instanz die X-Postion
12* Übergebe der Instanz die Y-Position
13* Falls die Instanz-ID noch nicht bekannt ist tue folgendes
14* Erstelle ein neues Objekt vom typ obj_BallofSteel_Recall
15* Trag die Instanz-ID des Servers in die Recall Liste ein
16* Trag die Instanz-ID des Servers und des Clienten in die Recall Map ein
17* Übergebe der Instanz die X-Postion
18* Übergebe der Instanz die Y-Position
19* Falls dies ein CMD_DEL befehl ist und tue folgendes um das zugehörige Objekt zu löschen
20* Lies die Instanz-ID des Server-Objektes aus
21* Suche mithilfe der Instanz-ID des Servers die Instanz-ID des Clienten
22* Falls die Instanz-ID schon bekannt ist tue folgendes
23* Lösche die zugehörige Instanz des Clienten
Wir erweitern das Draw Event um zu sehen welche Instanzen empfangen werden (optional):
Und das war es auch schon. Falls alles korrekt gemacht wurde müssten jetzt die Clients auch auf allen Clienten angezeigt werden und nicht nur auf dem Server.
Dieser Beitrag wurde bereits 10 mal editiert, zuletzt von Balls of Steel () aus folgendem Grund: Erweiterung des Tutorials