Netzwerk-Tutorial ohne DLL (Game Maker Studio)

    • Studio

      Netzwerk-Tutorial ohne DLL (Game Maker Studio)

      ---------------------------------------------------------------
      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

      1. /// Erstelle einen Server
      2. server = network_create_server( network_socket_tcp, 6510, 16 ); // Horcht am Port 6510 mit max 16 Spielern
      3. map_spieler = ds_map_create(); // Beinhaltet die Instanzen der verbundenen Spieler
      4. list_spieler = ds_list_create(); // Beinhaltet die Sockel der verbundenen Spieler

      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

      1. /// Netzwerkempfang
      2. var eventid = ds_map_find_value(async_load, "id");
      3. if(server == eventid) // wenn die Sockel-ID zum Server gehört kommt eine neue Verbindung rein, oder wird unterbrochen
      4. { var t = ds_map_find_value(async_load, "type");
      5. var sock = ds_map_find_value(async_load, "socket");
      6. var ip = ds_map_find_value(async_load, "ip");
      7. if(t == network_type_connect) // FALLS EINE VERBINDUNG HERGESTELLT WIRD
      8. { if!(ds_map_exists(map_spieler, sock)) // Falls der Spieler noch nicht existiert --> erstelle ihn
      9. { var inst = instance_create(room_width / 2, room_height / 2, obj_BallofSteel); // Setze die Spielfigur auf das Spielfeld
      10. ds_list_add (list_spieler, sock); // Setze den Spieler auf die Verbindungsliste der Sockel
      11. ds_map_add (map_spieler, sock, inst); // Verknüpfe in der Spielerliste den Client-Sockel mit der Client-Objekt-Instanz
      12. inst.sock = sock;
      13. inst.ip = ip;
      14. }
      15. }
      16. else // FALLS EINE VERBINDUNG ABGEBROCHEN WIRD
      17. { if(ds_map_exists(map_spieler, sock)) // Falls der Spieler existiert --> lösche ihn
      18. { inst = ds_map_find_value(map_spieler, sock); // Suche die Client-Objekt-Instanz
      19. var index = ds_list_find_index(list_spieler, sock); // Suche den Index des Sockels aus der Sockelliste
      20. ds_list_delete(list_spieler, index); // Lösche den Sockel aus der Sockelliste
      21. ds_map_delete(map_spieler, sock); // Lösche den Spieler von der Liste
      22. with(inst)
      23. { instance_destroy(); // Lösche die Spielfigur vom Spielfeld
      24. }
      25. }
      26. }
      27. }
      28. else // Alle anderen Sockel sind von Spielern und wir bekommen Daten von ihnen
      29. { var buff = ds_map_find_value(async_load, "buffer"); // Erstelle den Puffer in den die Daten kommen
      30. var cmd = buffer_read(buff, buffer_s16); // Auslesen des Befehls
      31. var sock = ds_map_find_value(async_load, "id"); // Auslesen der Sockel-ID des Clients als Schlüssel
      32. var inst = ds_map_find_value(map_spieler, sock); // Auslesen der Instanzenzuordung des Clients
      33. if(cmd == CMD_PING) // Wir wurden nur angepingt mehr nicht
      34. {
      35. }
      36. else if(cmd == CMD_POS) // Es kommen Koordinaten rein
      37. { inst.x = buffer_read(buff, buffer_s16 ); // X an Instanz übergeben
      38. inst.y = buffer_read(buff, buffer_s16 ); // Y an Instanz übergeben
      39. }
      40. }

      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:

      GML-Quellcode

      1. /// Draw INFORMATION
      2. draw_set_font(font_1);
      3. draw_set_color(c_black);
      4. draw_text(10, 10, INFO_CREDIT);
      5. draw_text(10, 25, "Version: Server 1.0");
      6. draw_text(10, 40, "Verbundene Clienten:");
      7. draw_text(170, 40, ds_list_size(list_spieler));

      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:

      GML-Quellcode

      1. /// Draw Position
      2. draw_self();
      3. draw_set_font(font_1);
      4. draw_set_color(c_gray);
      5. draw_text(x + 20, y - 15, x);
      6. draw_text(x + 20, y + 0, y);
      7. draw_text(x - 10, y + 15, ip);
      8. draw_text(x - 10, y + 30, sock);

      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. :pinch:
      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

      1. /// Stubs mal den Server an um zu sehen ob er lust hat was zu empfangen
      2. { buffer_seek(buffer, buffer_seek_start, 0); // Setze den Datenpuffer auf 0 zurück
      3. buffer_write(buffer, buffer_s16, CMD_PING); // Schreibe in den Puffer die Art der übertragung
      4. network_send_packet(client, buffer, buffer_tell(buffer)); // Verschicke den Puffer
      5. }

      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

      1. /// Melde dem Server die Position des Objekts
      2. /// argument0 = x
      3. /// argument1 = y
      4. { buffer_seek(obj_Client.buffer, buffer_seek_start, 0);
      5. buffer_write(obj_Client.buffer, buffer_s16, CMD_POS);
      6. buffer_write(obj_Client.buffer, buffer_s16, argument0);
      7. buffer_write(obj_Client.buffer, buffer_s16, argument1);
      8. network_send_packet(obj_Client.client, obj_Client.buffer, buffer_tell(obj_Client.buffer));
      9. }

      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

      1. /// Erstelle eine Verbindung zum Server
      2. client = network_create_socket( network_socket_tcp );
      3. network_connect( client, "127.0.0.1", 6510 ); // In diesem Fall verbinden wir nur zum LocalHost also zu uns selbst
      4. buffer = buffer_create( 256, buffer_grow, 1); // Datenpuffer für Netzwerkübertragungen
      5. 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:

      GML-Quellcode

      1. /// Draw INFORMATION
      2. draw_set_font(font_1);
      3. draw_set_color(c_black);
      4. draw_text(10, 10, INFO_CREDIT);
      5. draw_text(10, 25, "Version: Client 1.0");
      6. draw_text(10, 40, "Den Server bitte vor dem Clienten starten!");




      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:

      GML-Quellcode

      1. /// Sende die Position des Balls an den Server
      2. Send_Pos(x, y);

      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:

      GML-Quellcode

      1. /// Bewege dich nach links
      2. x = x - 3;

      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:

      GML-Quellcode

      1. /// Bewege dich nach oben
      2. y = y - 3;

      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:

      GML-Quellcode

      1. /// Bewege dich nach rechts
      2. x = x + 3;

      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:

      GML-Quellcode

      1. /// Bewege dich nach unten
      2. y = y + 3;

      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:

      GML-Quellcode

      1. /// Falls das Objekt ausserhalb des Raumes gerät setzte es in die Mitte zurück
      2. x = room_width / 2;
      3. y = room_height / 2;

      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. :pinch:
      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
      ---------------------------------------------------------------


      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:

      GML-Quellcode

      1. buffer = buffer_create( 256, buffer_grow, 1); // Datenpuffer für Netzwerkübertragungen

      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

      1. /// Sende an die Clienten
      2. var_size = ds_list_size(list_spieler);
      3. if(var_size > 0) // 1*
      4. { for(i = 0; i < var_size; i++) // 2*
      5. { var_inst = ds_map_find_value(map_spieler, ds_list_find_value(list_spieler, i)); // 3*
      6. buffer_seek(buffer, buffer_seek_start, 0); // 4*
      7. buffer_write(buffer, buffer_s16, CMD_RECALL); // 5*
      8. buffer_write(buffer, buffer_s16, var_size -1); // 6*
      9. for(ii = 0; ii < var_size; ii++) // 7*
      10. { var_id = ds_map_find_value(map_spieler, ds_list_find_value(list_spieler, ii)); // 3*
      11. if(var_inst != var_id) // 8*
      12. { buffer_write(buffer, buffer_f32, var_id); // 9*
      13. buffer_write(buffer, buffer_s16, var_id.x); // 10*
      14. buffer_write(buffer, buffer_s16, var_id.y); // 11*
      15. }
      16. }
      17. network_send_packet(var_inst.sock, buffer, buffer_tell(buffer)); // 12*
      18. }
      19. }

      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

      1. ...
      2. else // FALLS EINE VERBINDUNG ABGEBROCHEN WIRD
      3. { if(ds_map_exists(map_spieler, sock)) // Falls der Spieler existiert --> lösche ihn
      4. { inst = ds_map_find_value(map_spieler, sock); // Suche die Client-Objekt-Instanz
      5. var index = ds_list_find_index(list_spieler, sock); // Suche den Index des Sockels aus der Sockelliste
      6. // Start Erweiterung: sende CMD_DEL an Clienten
      7. var_size = ds_list_size(list_spieler);
      8. for(i = 0; i < var_size; i++) // 1*
      9. { var_inst = ds_map_find_value(map_spieler, ds_list_find_value(list_spieler, i)); // 2*
      10. if(var_inst != inst) // 3*
      11. { buffer_seek(buffer, buffer_seek_start, 0); // 4*
      12. buffer_write(buffer, buffer_s16, CMD_DEL); // 5*
      13. buffer_write(buffer, buffer_f32, inst); // 6*
      14. network_send_packet(var_inst.sock, buffer, buffer_tell(buffer)); // 7*
      15. }
      16. }
      17. // Ende Erweiterung
      18. ds_list_delete(list_spieler, index); // Lösche den Sockel aus der Sockelliste
      19. ds_map_delete(map_spieler, sock); // Lösche den Spieler von der Liste
      20. with(inst)
      21. { instance_destroy(); // Lösche die Spielfigur vom Spielfeld
      22. }
      23. }
      24. }
      25. ...

      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):

      GML-Quellcode

      1. var_size = ds_list_size(list_spieler);
      2. for(i = 0; i < var_size; i++)
      3. { draw_text(10, 100 + (i * 15), ds_map_find_value(map_spieler, ds_list_find_value(list_spieler, i)));
      4. }

      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:

      GML-Quellcode

      1. list_recall = ds_list_create();
      2. map_recall = ds_map_create();
      3. server = network_create_server( network_socket_tcp, 6510, 16 ); // Horcht am Port 6510 mit max 16 Spielern




      Nun zum aufwendigsten teil, dem Networking Event. Dieses müssen wir neu erstellen und mit folgendem Code füttern:

      GML-Quellcode

      1. /// Netzwerkempfang
      2. var eventid = ds_map_find_value(async_load, "id");
      3. if(server != eventid) // 1*
      4. { var buff = ds_map_find_value(async_load, "buffer"); // 2*
      5. var cmd = buffer_read(buff, buffer_s16); // 3*
      6. var sock = ds_map_find_value(async_load, "id"); // 4*
      7. if(cmd == CMD_RECALL) // 5*
      8. { var_recall = buffer_read(buff, buffer_s16); // 6*
      9. for(i = 0; i < var_recall; i++) // 7*
      10. { var_id = buffer_read(buff, buffer_f32); // 8*
      11. var_id_obj = ds_map_find_value(map_recall, var_id); // 9*
      12. if(var_id_obj > 100000) // 10*
      13. { var_id_obj.x = buffer_read(buff, buffer_s16); // 11*
      14. var_id_obj.y = buffer_read(buff, buffer_s16); // 12*
      15. }
      16. else // 13*
      17. { var_id_obj = instance_create(10, 10, obj_BallofSteel_Recall); // 14*
      18. ds_list_add(list_recall, var_id); // 15*
      19. ds_map_add(map_recall, var_id, var_id_obj); // 16*
      20. var_id_obj.x = buffer_read(buff, buffer_s16); // 17*
      21. var_id_obj.y = buffer_read(buff, buffer_s16); // 18*
      22. }
      23. }
      24. }
      25. if(cmd == CMD_DEL) // 19*
      26. { var_id = buffer_read(buff, buffer_f32); // 20*
      27. var_id_obj = ds_map_find_value(map_recall, var_id); // 21*
      28. if(var_id_obj > 100000) // 22*
      29. { with(var_id_obj)
      30. instance_destroy(); // 23*
      31. }
      32. }
      33. }

      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):

      GML-Quellcode

      1. var_size = ds_list_size(list_recall);
      2. draw_text(10, 80, var_size);
      3. for(i = 0; i < var_size; i++)
      4. { draw_text(10, 100 + (i * 15), ds_list_find_value(list_recall, i));
      5. }



      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.
      Bilder
      • Netzwerk-Tutorial.jpg

        66,92 kB, 810×451, 505 mal angesehen
      • Netzwerk-Tutorial-Configs.jpg

        113,97 kB, 796×463, 326 mal angesehen
      • Tutorial_ext.jpg

        140,63 kB, 656×890, 201 mal angesehen
      Dateien

      Dieser Beitrag wurde bereits 10 mal editiert, zuletzt von „Balls of Steel“ () aus folgendem Grund: Erweiterung des Tutorials

      Septimus417 schrieb:

      Aber wieso sieht man nur beim Server die 2 anderen? Man kann sich nicht gegenseitig sehen??!

      Balls of Steel schrieb:

      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.

      Wie schon gesagt, der einfachheit halber. So sieht man eindeutig das die Daten vom Client zum Server gesendet werden.
      Und wenn man das ganze verstanden hat sollte es auch kein Problem sein die Daten der Spieler auch wieder an die Clienten zurück zu senden.

      Ich hab momentan wenig Zeit, aber ich werde wie versprochen das Tutorial erweitern. Ich lege mich jetzt nur nicht fest wann.
      Sieht interessant aus mit den Netzwerkfunktionen des Game Makers Studio, allerdings frage ich mich, wie es performance-mäßig aussieht? Wenn du sie jetzt vergleichst mit der 39dll ? Sieht auf dem ersten Blick doch ziemlich Umfangreich aus.
      Scheinst aber einiges auf dem Kasten zuhaben.

      Ich arbeite zurzeit auch an einem Multiplayer RPG, dass einzigste was mit dem MMO Prinzip gleich ist, dass sich theoretisch unendliche viele Clients verbinden könnten. Da ich aber nicht an einem Online MMO arbeite, ist es nicht das selbe, wie deine Engine, verfolgt aber das gleiche Prinzip und die gleiche Komplexität.
      Das schlimme ist, dass man manchmal vieles komplett überarbeiten muss, weil man unlogisch programmiert hat oder Schritte vergessen hat und muss teilweise sehr viel Code umschreiben. Das kann ganz schön viel Kopfzerbrechen machen. So sehr das ich teilweise wirklich 3 Stunden an der selben Aufgabe hänge.

      Allerdings frage ich mich wie das mit den NPC's funktioniert, also technisch, auf jeden Fall muss jeder Client jeden NPC seine Daten wissen und dann auch noch die NPC, die bevor du gespawnt bist, joinen. So im ganzen, verliere ich die teilweise auch die Übersicht dabei. Ich will aber nicht aufgeben, wie ich es bis jetzt immer getan habe. Allerdings glaube ich mit viel Pfleiß kann man alles schaffen, muss mich warscheinlich noch näher damit aussetzten. Das Problem ist auch teilweise, das ich manchmal garnicht weiß wie es sofort weitergeht, weil ich viel und lange überlegen muss.

      Im ganzen finde ich schon mal toll, dass du ein Tutorial hier machst, vorallem, weil das die meisten nicht können = Multiplayer. Vielleicht mache ich ein Komplexeres Online Tutorial über die 39dll, wenn ich wirklich komplett dahinter gekommen bin.

      PS: Ich wette dein Aluh Gött wird ein echter GM-D Kracher!;)
      Mono C# / SFML.Net <3
      Da sich das jetzt scheinbar viele Anfänger als Vorbild nehmen, sollte im Tutorial wenigstens mal erwähnt werden, dass die Art der Übertragung (x und y Position im Step Event) nicht wirklich praktikabel ist. Die Übertragungen sollten maximal bei einer tatsächlichen Positionsänderung, besser aber noch nur zu Beginn und Ende einer Bewegung stattfinden.

      Abgesehen davon auf jeden Fall ein gutes Tutorial um die grundlegenden Mechanismen zu verstehen. :)
      Weiß jemand, wie viele daten (zahlen) man maximal so pro sekunde versenden sollte, das es nicht laggt?
      Kopiere dies in deine Signatur, um es in deiner Signatur zu haben.
      Achtung: Dieser Beitrag läuft ende des Monats ab, bitte lese ihn noch vor dem Monatswechsel...
      Nach langer zeit wieder im Forum aktiv :D
      Weiß jemand, wie viele daten (zahlen) man maximal so pro sekunde versenden sollte, das es nicht laggt?
      Du bist pro Packet auf die MTU begrenzt, alles über der MTU muss fragmentiert werden. Bis es anfängt zu laggen hängt von der Bandbreite ab und ob du UDP oder TCP nutzt (theoretisch?).
      wupto.net/ Nicht meine Seite!
      We love Koalas.

      GM-D-Spam-o-Meter: 32%
      Ich habe mir ein Multiplayer programmiert und es funktioniert auch super in localhost bzw. eigene Rechner. Aber über Hamachi erkennt die Client die Server irgendwie nicht. Egal ob ich die IP 127.0.0.1 oder meine IP aus Hamachi benutze. Es will einfach nicht funktionieren D: Oder funktionieren Game Maker's Spiele unter Hamachi nicht?
      Ihr stinkt.
      Doch klar, sogar sehr gut!
      Sicher, das du die ip des Mitspielers richtig eingegeben hast?
      Kopiere dies in deine Signatur, um es in deiner Signatur zu haben.
      Achtung: Dieser Beitrag läuft ende des Monats ab, bitte lese ihn noch vor dem Monatswechsel...
      Nach langer zeit wieder im Forum aktiv :D
      Ich habe es Jetzt gerade auch Getestet und kam auch Irgendwie nicht rein mit der Hamachi. Sollte aber laufen, Wenn ich es richtig gelesen habe, In dem Englischen GameMaker Studio Bereich. Jedenfalls finde ich Deinen Tutorial zum Multiplayer aufbauen, Einfach Toll!

      Larsi schrieb:

      Doch klar, sogar sehr gut!
      Sicher, das du die ip des Mitspielers richtig eingegeben hast?



      Jap, man kann selber die IP-Adresse eingeben, also haben wir alle Mögliches versucht -> Kein Erfolg. Es geht nur unter meine eigenen Rechner. Man muss die IP von Hamachi nehmen, richtig?

      //EDIT

      Ich habe Port forwarding/freischalten/oderwasauchihrimmernennt ohne Hamachi gemacht -> Auch keine Erfolg. Mein Freund ist ein Server-Expert und bei ihnen geht es auch nicht. Irgendwas ist doch was faul! Wieso funktioniert es unter localhost, aber außerhalb nicht? oO
      Ihr stinkt.

      Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Chinafreak“ ()

      Habe eine Frage zum Buffer.
      Du hast ja drei Werte die du mit dem gleichen Buffer schickst:

      GML-Quellcode

      1. buffer_write(obj_Client.buffer, buffer_s16, CMD_POS);
      2. buffer_write(obj_Client.buffer, buffer_s16, argument0);
      3. buffer_write(obj_Client.buffer, buffer_s16, argument1);


      woher weiss der Game maker dann welcher Wert wohin gehört beim Lesen?:

      GML-Quellcode

      1. inst.x = buffer_read(buff, buffer_s16 );
      2. inst.y = buffer_read(buff, buffer_s16 );


      Wie kann ich mir so einen Buffer vorstellen?

      Husi012 hat mich nicht mehr in seiner Signatur, ich bin trotzdem noch fame.
      @Dufner

      In der reihenfolge in der du sie verschickst musst du die Werte auch wieder auslesen.

      Ich habe es so gemacht, das der erste Wert (s_16) mir vorgibt was da überhaupt ankommt, und daraus schlussfolgert das Empfängerprogramm (switch tabelle) welche Werte in welcher reihenfolge es auszulesen hat.

      Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Balls of Steel“ ()