|
Multiplayer |
Top Previous Next |
|
In diesem Monat geht es um die Clients und wie wir diese auseinanderhalten können. Unsere vorigen Beispiele funktionierten gut für zwei Spieler, aber wie schreiben wir ein Spiel, bei dem bis zu 128 Clients zugleich mitspielen können? Und wie entfernen wir beispielsweise Client 123, der sich immer nur versteckt und anderen in den Rücken schießt? Dies sind nur einige der Fragen, die hier beantwortet werden.
Beginnen wir mit dem multiplayer12.c Skript, welches als Grundlage für alle Multiplayerprojekte dienen kann.
#include <acknex.h> #include <default.c>
var server_only = 0; var client_only = 0; var client_server = 0;
Beachten Sie zunächst die drei Variablen, die deklariert wurden. server_only wird auf einem dedicated Server auf 1 gesetzt, client_only bezeichnet einen Rechner, der nur als Client fungiert und client_server steht auf 1, wenn das Spiel als Server und Client zugleich läuft. Wir werden diese Variablen nicht verwenden, aber Sie können Sie nutzen, um spezifischen Code auszuführen, der nur auf dem Server oder nur auf reinen Clients etc. laufen soll.
Die nächsten beiden Variablen speichern die ID des Clients. Warum zwei Variablen? Player_id ist eine globale Variable, mit der auch die Anzahl der verbundenen Clients ermittelt werden kann, wohingegen my_id als lokale Variable nur für jeweils einen Client relevant ist. Ich habe ein Panel verwendet, um diese auf den Clients anzuzeigen.
var player_id = 0; var my_id = 0;
PANEL* id_pan = { digits(20, 20, 3 ,* , 1, my_id); flags = visible; }
function server_event() { if (event_type == EVENT_JOIN) { player_id += 1; send_var(player_id); } // take care of the rest of the messages here }
Die Funktion server_event läuft jedes Mal, wenn der Server eine Nachricht von einem der Clients empfängt. Falls ein neuer Client dem Spiel beitritt, wird player_id um einen erhöht und dieser Wert über das Netzwerk verschickt. Der Server setzt also player_id auf 1, der erste Client setzt den Wert auf 2, der zweite auf 3 usw.
Die main Funktion ähnelt der aus den vorigen Multiplayer Projekten; beachten Sie, dass ich anstelle von Strings und Texten die Nachrichten mit Hilfe von video_window als Titel der Fenster anzeige.
function main() { on_server = server_event; if (!connection) // can't find a server? (connection = 0) { video_window(NULL, NULL, 16, "Can't find any server. Please try again later."); wait (-4); sys_exit(NULL); }
if (connection == 1) // this instance of the demo runs as a dedicated server? { server_only = 1; video_window(NULL, NULL, 16, "Dedicated server"); // put the rest of your server code here }
if (connection == 3) // this instance of the demo runs as a client and server at the same time? { client_server = 1; player_id += 1; // we set player_id = 1 for this client (the server is also a client) my_id = player_id; // store player_id locally video_window(NULL, NULL, 16, "Client and Server"); // put the rest of your client / server code here } if (connection == 2) // this instance of the demo runs as a client? { client_only = 1; video_window(NULL, NULL, 16, "Client"); while (!player_id) {wait (1);} // wait until you receive the updated player_id value from the server my_id = player_id; // store player_id locally // put the rest of your client code here } }
Starten Sie ein Mal server.bat und dann mehrere Male client.bat um mehrere Clients zu erzeugen; Sie werden sehen, dass jeder eine eigene ID erhält. Falls Sie einen deidcated Server betreiben (indem Sie z.B. die "-cl" Option in server.bat entfernen), hat der erste Client die ID 1, der zweite die Nummer 2 usw.
Wie begrenzen wir die Anzahl an Clients, die sich mit dem Server verbinden können? Starten Sie die Demo aus dem \multiplayer13 Ordner und Sie werden merken, dass Sie nicht mehr als 3 Clients verwenden können. Jeder weitere Client, der sich mit dem Server verbinden will, erhält einen my_id Wert von 0.
Schauen wir uns den Code an, der dafür verantwortlich ist.
#include <acknex.h> #include <default.c>
var server_only = 0; // these variables are defined for further use var client_only = 0; var client_server = 0;
var player_id = 0; var my_id = 0;
PANEL* id_pan = { digits(20, 20, 3 ,* , 1, my_id); flags = visible; }
function main() { on_server = server_event; if (!connection) // can't find a server? (connection = 0) { video_window(NULL, NULL, 16, "Can't find any server. Please try again later."); wait (-4); sys_exit(NULL); } if (connection == 1) // this instance of the demo runs as a dedicated server? { server_only = 1; video_window(NULL, NULL, 16, "Dedicated server"); // put the rest of your server code here } if (connection == 3) // this instance of the demo runs as a client and server at the same time? { client_server = 1; player_id += 1; // we set player_id = 1 for this client (the server is also a client) my_id = player_id; // store player_id locally video_window(NULL, NULL, 16, "Client and Server"); // put the rest of your client / server code here } if (connection == 2) // this instance of the demo runs as a client? { client_only = 1; video_window(NULL, NULL, 16, "Client"); while (!player_id) {wait (1);} // wait until you receive the updated player_id value from the server my_id = player_id; // store player_id locally // put the rest of your client code here } }
Der einzige Teil, der sich gravierend verändert hat, ist der folgende.
function server_event(STRING* client_name) { if (event_type == EVENT_JOIN) // a new client has joined the game? { player_id += 1; // then increment player_id if (player_id > 3) // don't allow more than 3 players to connect to the server { client_drop(client_name); } else { send_var(player_id); // send the new player_id value over the network } } // take care of the rest of the messages here }
Die Funktion server_event() ist die Eventfunktion des Servers, die jedes Mal aufgerufen wird, wenn der Server eine Nachricht von einem Client erhält. Beachten Sie, dass die Funktion den Namen des Clients, der sich verbinden will als Parameter erhält. Falls ein neuer Client beitritt, erhöhen wir player_id wie üblich; falls dieser Wert aber größer ist als 3 (mehr als 3 Spieler wollen mitspielen), sorgt die Funktion "client_drop" dafür, dass sich niemand sonst verbinden kann.
Der Code der folgenden Demo findet sich in der Datei multiplayer14.c; damit können wir nach Belieben störende Spieler hinauswerfen.
Wie Sie sehen hat die Instanz des Spiels, die zugleich Client und Server ist ein spezielles Panel mit 8 "KICK" Knöpfen. Der erste wirft den ersten Client hinaus, der zweite den zweiten Client usw. Sehen wir uns den Code an.
#include <acknex.h> #include <default.c>
var server_only = 0; // these variables are defined for further use var client_only = 0; var client_server = 0;
var player_id = 0; var my_id = 0; var kicked_client = 1000; // set kicked_client to a value that's bigger than the maximum number of clients
Die Variable kicked_client hat anfangs einen großen Wert; später wird dieser der Wert des hinauzuwerfenden Clients zugewiesen, also z.B. 3, falls der Server Client Nummer 3 hinauswerfen will.
BMAP* kick1_pcx = "kick1.pcx"; BMAP* kick2_pcx = "kick2.pcx"; BMAP* pointer_pcx = "pointer.pcx";
PANEL* id_pan = { digits(20, 20, 3 ,* , 1, my_id); flags = visible; }
PANEL* kick_pan = { pos_x = 20; pos_y = 300; button = 0, 0, kick1_pcx, kick1_pcx, kick2_pcx, kick_clients, null, null; button = 0, 20, kick1_pcx, kick1_pcx, kick2_pcx, kick_clients, null, null; button = 0, 40, kick1_pcx, kick1_pcx, kick2_pcx, kick_clients, null, null; button = 0, 60, kick1_pcx, kick1_pcx, kick2_pcx, kick_clients, null, null; button = 0, 80, kick1_pcx, kick1_pcx, kick2_pcx, kick_clients, null, null; button = 0, 100, kick1_pcx, kick1_pcx, kick2_pcx, kick_clients, null, null; button = 0, 120, kick1_pcx, kick1_pcx, kick2_pcx, kick_clients, null, null; button = 0, 140, kick1_pcx, kick1_pcx, kick2_pcx, kick_clients, null, null; }
Beachten Sie, dass dieses Panel nur auf dem Server sichtbar ist; die 8 Knöpfe teilen sich eine Funktion kick_clients(button_number).
function kick_clients(button_number) { kicked_client = button_number; // store the button_number value inside kicked_client send_var(kicked_client); // now send kicked_client over the network }
Diese Funktion speichert die Nummer des gedrückten Knopfes in der Variable kicked_client und sendet diese über das Netzwerk.
function server_event(STRING* client_name) { if (event_type == EVENT_JOIN) // a new client has joined the game? { player_id += 1; // then increment player_id send_var(player_id); // send the new player_id value over the network } // take care of the rest of the messages here }
Die Funktion server_event hat sich nicht geändert. Dieses Mal gibt es aber eine client_event Funktion.
function client_event() { if (kicked_client == my_id) { sys_exit(NULL); } // take care of the rest of the client messages here }
Die Funktion client_event läuft jedes Mal wenn der Client eine Nachricht erhält. Wenn "kicked_client" und "my_id" übereinstimmen, dann möchte der Server diesen Client entfernen, also wird die Engine beendet. Ich habe es dem Server in diesem Beispiel nicht erlaubt, sich selbst hinauszuwerfen, aber wenn Sie das möchten, können Sie entsprechenden Code in der server_event( ) Funktion eintragen.
function init_mouse() { mouse_mode = 2; mouse_map = pointer_pcx; while (1) { vec_set(mouse_pos, mouse_cursor); wait(1); } }
function main() { on_server = server_event; on_client = client_event; if (!connection) // can't find a server? (connection = 0) { video_window(NULL, NULL, 16, "Can't find any server. Please try again later."); wait (-4); sys_exit(NULL); } if (connection == 1) // this instance of the demo runs as a dedicated server? { server_only = 1; video_window(NULL, NULL, 16, "Dedicated server"); // put the rest of your server code here } if (connection == 3) // this instance of the demo runs as a client and server at the same time? { client_server = 1; set (kick_pan, VISIBLE); // display kick_pan only on the server init_mouse(); // display the mouse pointer only on the server player_id += 1; // we set player_id = 1 for this client (the server is also a client) my_id = player_id; // store player_id locally video_window(NULL, NULL, 16, "Client and Server"); // put the rest of your client / server code here } if (connection == 2) // this instance of the demo runs as a client? { client_only = 1; video_window(NULL, NULL, 16, "Client"); while (!player_id) {wait (1);} // wait until you receive the updated player_id value from the server my_id = player_id; // store player_id locally // put the rest of your client code here } }
Die main Funktion hat sich nicht sehr verändert. Wir machen kick_pan sichtbar und zeigen den Mauszeiger nur an, wenn connection den Wert 3 hat, also im Fall einee gemeinsamen Server / Client Instanz.
Und so ist es geschafft. Nach 6 Multiplayer Workshops und 14 spielbaren Demos ist es Zeit, sich einem anderen Aspekt der Spieleentwicklung zuzuwenden. Ich weiß, dass wir nicht alle Bereiche der Multiplayer Thematik abgedeckt haben, aber das hätte auch Jahre gedauert. Multiplayer Programmierung ist keine leichte Aufgabe, aber ich hoffe, dass die Informationen und der von mir bereitgestellte Code es Ihnen erlaubt, ein eigenes Multiplayerprojekt zu beginnen.
Wir sehen uns im nächsten Monat zum neuen großen Projekt!
|