Workshop 1: Auf- und Abbau einer Verbindung


In diesem HowTo wird Ihnen erklärt, wie Sie eine Verbindung richtig auf und abbauen und die Verbindungsevents benutzen können! Alle Beispiele sind in Lite-C geschrieben, da sich der C-Script Code nicht sehr viel unterscheiden würde.

Initialisieren eines Servers


Sehen wir uns als erstes an, wie ein Server initialisiert wird und wozu man die Events benötigt:

//Lite-C:

function client_connected(var sender, char* msg, var length)
{
//...
}

function client_disconnected(var sender, char* msg, var length)
{
//...
}
function init_server()
{
enet_init();
enet_svset_event(EVENT_CONNECTED, client_connected);
enet_svset_event(EVENT_DISCONNECTED, client_disconnected);
enet_init_server(2300, 4, "PaSsWoRd!");
}

Ok, sehen wir uns die Funktion init_server() an. Zuerst wird ENet initialisiert, da unser Server ENet benutzt, müssen wir dies vor dem Initialisieren des Servers aufrufen. Danach legen wir die Funktionen client_connected() und client_disconnected() auf die CONNECTED und DISCONNECTED Events. Danach wird der Server initialisiert. Der Server lauscht auf den Port 2300 und erlaubt maximal 4 mögliche Verbindungen. Außerdem dürfen sich nur Clients verbinden, die das korrekte Passwort übergeben.
Ok so weit so gut. Aber was bedeuten nun die 2 Events? Ganz einfach, die Funktion die auf EVENT_CONNECTED gelegt ist, also client_connected() in unserem Fall, wird dann ausgeführt, wenn sich ein neuer Client verbunden hat. Logischerweise wird dann die Funktion, die auf EVENT_DISCONNECTED liegt, ausgeführt, sobald ein Client die Verbindung unterbrochen hat. Jede Funktion, die einem Event zugewiesen wurde, sollte als Parameter "var sender, char* msg, var length" aufweisen. sender würde in unserem Fall immer die ClientID (mehr dazu später) des Clients, der die Verbindung aufgebaut/unterbrochen hat, enthalten. msg und length werden bei diesen Events nicht gebraucht!

Um einen Server wieder zu deinitialisieren, brauchen Sie nur enet_destroy_host() aufrufen!

Es können auch mehrere Server gleichzeitig initialisiert werden. Jedoch nur ein Server pro Applikation. D.h. wenn Sie mehrere Server verwenden wollen, müssen Sie auch mehrere Applikationen starten. Die Server dürfen nicht alle auf den selben Port lauschen. Mehrere Server werden benötigt, um mehrere Zonen zu erstellen. Jede Zone stellt hierbei einen Server (mit einem speziellen Port) dar. Mehr dazu in einem anderen HowTo!

Initialisieren eines Clients


Nun sehen wir uns an, wie ein Client initialisiert wird:

//Lite-C:

function connected(var sender, char* msg, var length)
{
//...
}

function disconnected(var sender, char* msg, var length)
{
//...
}
function init_client()
{
enet_init();
enet_clset_event(EVENT_CONNECTED, connected);
enet_clset_event(EVENT_DISCONNECTED, disconnected);
enet_init_client("localhost", 2300, "PaSsWoRd!");
}

Wie Sie sehen, unterscheidet sich der Code nicht viel von dem Initialisieren eines Servers. Das EVENT_CONNECTED wird ausgeführt, sobald man eine erfolgreiche Verbindung zum Server aufgebaut hat, EVENT_DISCONNECTED wird ausgeführt, wenn die Verbindung unterbrochen wurde. Die Parameter sender und msg werden nicht benötig. Per enet_init_client() wird ein Client initialisiert. Er verbindet sich über den Port 2300 mit einem Server der sich auf dem selben Rechner befindet (=> "localhost").

Im Gegensatz zum Server können mehrere Clients gleichzeitig am selben Rechner gestartet werden ohne einen anderen Port benutzen zu müssen. Aber es darf nur ein Client pro Applikation gestartet werden.

Die Verbindung zu einem Client kann nur der Server unterbrechen. Dafür muss er einfach die Funktion enet_disconnect_client() aufrufen. Der Client kann die Verbindung nur unterbrechen, indem er enet_destroy_host() aufruft. Der Nachteil dabei ist aber, dass ein gewisses Timeout vergeht, ehe das Client Disconnected Event am Server ausgeführt wird!

Senden von Daten


Nun da Sie wissen, wie man eine Verbindung zwischen einem Server und einem Client aufbaut, sind Sie nur noch einen Funktionsaufruf davor entfernt Daten auszutauschen. Es gibt viele Funktionen, mit denen Daten ausgetauscht werden können. Wir sehen uns eine in einem Beispiel an. Alle anderen funktionieren vom Prinzip her gleich:

//Lite-C:
var test_var = 0;

function client_to_server()
{
test_var = 10;
enet_sendto(test_var, SERVER);
}

function send_to_client0()
{
test_var = 20;
enet_send_var(test_var, 0);
}

Wenn die Funktion client_to_server() vom Client ausgeführt wird, wird die Variable test_var auf 10 gesetzt und anschließend zum Server gesendet. D.h. die Variable test_var hat am Server und am Client den Wert 10. Sollten sich später weitere Clients verbinden oder sind bereits weitere verbunden, dann hat bei diesen Clients die Variable noch immer den Wert 0. Der Grund ist der, dass der neue Wert der Variable nur zum Server gesendet wurde. Um die Variable zu allen verbundenen Clients zu senden, verwenden Sie für den 2. Parameter BROADCAST.
Wird die 2. Funktion vom Server ausgeführt, so sendet dieser den Wert 20 der Variable test_var zum Client mit der ClientID 0. Die ClientID kann am Client durch enet_get_clientid() ermittelt werden. Außerdem wird sie bei Eventfunktionen wie dem Client Connected Event übergeben. Nach Ausführen der Funktion hat test_var also am Server und am Client mit der ClientID 0 den Wert 20.
Die Funktion könnte aber auch von einem Client ausgeführt werden. Dann hat test_var am Sender und Empfänger den Wert 20.

Man kann also mit dem 2. Parameter den Empfänger der Daten angeben wobei SERVER immer der Server ist und BROADCAST immer ein Broadcast (= senden an alle) ist.

Achtung: Wenn ein Server und ein Client miteinander Daten ausgetauscht haben und ein neuer Client verbindet sich, so werden die neuen Daten nicht automatisch zum Client übertragen! D.h. wie in unserem Beispiel würde test_var trotzdem 0 sein, auch wenn alle anderen Clients den Wert 10 gespeichert haben. Sie können die neuen Daten aber ganz einfach vom Server zum Client senden, sobald das Client Connected Event am Server ausgeführt wird.

Schlussworte


Mit dem Wissen, dass Sie gerade erworben haben ist es ihnen bereits möglich einfache 2D Spiele wie Pong zu erstellen. Ein passendes Beispielskript finden Sie auf der Downloadseite von ANet. Aber bitte beachten Sie, dass der Code so optimiert wurde, dass er wenig Traffic (= Datenverkehr) verursacht. Dadurch ist er schwieriger verständlich als "normaler" Code.
Als nächsten Schritt sollten Sie sich den 2. Workshop "Simple 3D Chat" durchlesen. In diesem wird das soeben gelernte noch einmal wiederholt bzw. an einem praktischen Beispiel angewandt. Zusätzlich lernen Sie, wie Sie Entities in Ihrem Multiplayer verwenden können und wie Sie ein einfaches Chat erstellen können. Auch Themen wie Levelwechsel und Dead-Reckoning werden hier angeschnitten!