DelphiX-Tutorial für Anfänger
Ursprünglich erstellt von MiGa Mail Homepage
(Danke!!)
Design-Anpassung und Veränderung von Simon
Kapitel 9
In diesem Kapitel geht es um DirectPlay - Florian Sievert zeigt
uns, wie man einen Chat programmiert.
Tutorial für DirectPlay - Grundkenntnisse
I. Theorie und Praxis
Ich werde nun einfach mal versuchen zu erklären, wie man
DirectPlay verwendet und wie es funktionieren soll. Dieser Part
besteht nur aus purer Theorie! Wer sich gerne mit Praxis
beschäftigt, der kann sich das beiliegende Beispielprogramm ansehen.
Es handelt sich dabei um ein kleines Chat-Programm und es sollte
eigentlich leicht verständlich sein. Ich schreibe absichtlich alles
sehr vereinfacht und detailliert, damit auch relativ unerfahrene
Programmierer ihre eigenen Netzwerkprogramme schreiben können. Ich
selbst weiß nur zu gut, wie es ist, wenn man mit einem Profitutorial
konfrontiert wird und anschließend meist weniger weiß als vorher.
Deshalb sollten alle Fortgeschrittenen Verständnis für unsere
"Neuen" haben und sich nicht beleidigt fühlen, wenn erklärt wird,
was ein CHAR ist ...
II. "Wir erstellen ein Interface"
OK! Fangen wir an! Wir gehen davon aus, dass wir vor uns ein
einfaches leeres Formular haben und erstellen darauf nun einfach die
Komponente DXPlay von DelphiX und nennen diese DPlay. Dies ist schon
praktisch der erste Schritt um Zugang zu DirectPlay zu bekommen.
Ohne Komponenten müssten wir uns erst stundenlang ein Interface
basteln, doch diese Arbeit wurde uns Gott sei Dank schon abgenommen.
Als nächstesmuss nur noch DirectPlay initialisiert werden; dies
geschieht ganz leicht mit DPlay.Open; Nun erscheint für den
Anwender ein Dialog in dem er die Verbindungsart auswählen und
seinen Namen und den Host eingeben kann. Hier muss man sich um
nichts weiter kümmern; auch diese Arbeit wird uns abgenommen.
III. Definieren eines Nachrichtentyps
Als nächstes sollte man erst mal verschiedene Nachrichtenarten
auswählen, die DirectPlay dann später verwenden soll. Hier sind
seiner Phantasie praktisch keine Grenzen gesetzt, man sollte nur
aufpassen,dass diese Strukturen nach Möglichkeit nicht zu groß
werden, aber dies sollte eigentlich kein Problem darstellen, außer
man will Festplattenimages übers Netz schicken... ;)
Nehmen
wir einfach mal an, dass wir ein Chat-Programm schreiben wollen. Als
erstes müssen wir uns überlegen, was wollen wir eigentlich
verschicken. Die Antwort ist eigentlich ganz simpel: einen
String. Jetzt muss man allerdings wissen, dass so ein
Delphi-String sehr groß ist und sich nicht dazu eignet, über ein
Netzwerk geschickt zu werden. Wir haben nun natürlich die
Möglichkeit, diesen String zu begrenzen, sodass der Anwender immer
nur eine begrenzte Anzahl von Zeichen (z.B. 100) verschicken kann.
Dies wäre allerdings keine sehr elegante Lösung dieses Problems, da
der User nicht sehr glücklich sein wird, wenn er plötzlich nur die
Hälfte der Nachricht bekommt, weil die andere einfach gelöscht wird.
Deshalb wäre es besser, ein ARRAY OF CHAR zu verschicken. Je mehr
Zeichen der User eingibt, desto mehr werden
verschickt. Definieren wir nun einfach eine solche Message: TYPE
TDXChatMessage=RECORD
dwType:DWORD;
Len:WORD;
Msg:ARRAY[0..0] OF Char;
END;
"Moment!" werden Sie sich jetzt sicher fragen! Was hat eigentlich
dwType dort zu suchen?! Diese Variable wird benötigt, um später zu
erkennen, um welchen Typ der empfangenen Nachricht es sich
eigentlich handelt. Dieser Typmuss immer in einer Nachricht sein! Er
ist das absolute Minimum!. In Len wird die Länge der zu
übermittelnden Nachricht gespeichert; man könnte nun natürlich
ermitteln, wie lang die Nachricht ist und könnte sich diese Variable
schenken, aber wir wollen das Programm doch nicht zu komplex werden
lassen;-) Msg ist die eigentliche Nachricht die geschickt werden
soll und besteht aus einer Kette von CHAR (einzelnen Zeichen).
Sicherlich wundern Sie sich, warum ihr Bereich von 0 bis 0 geht. Wir
sagen damit einfach nur,dass die Nachricht einfach nur 0 Zeichen
lang sein soll; wenn mehr erforderlich ist werden wir diesen Bereich
vergrößern, sodass wirklich nur die Nachricht geschickt wird und
nicht ein Haufen von Leerzeichen.
Sehr geschickt ist es, vor allem bei großen Projekten die mehrere
verschiedene Nachrichten verschicken, die einzelnen ID (dwType) der
Typen als Konstanten festzuhalten. CONST
DX_ChatMessage = 1;
Nun ist das Formelle erledigt und wir können uns nun darum
kümmern, Nachrichten zu verschicken oder natürlich auch zu
empfangen. Wir fangen hierbei mit dem Empfangen an, weil das Senden
ein klein wenig schwieriger ist. (Aber nur ein gaaanz klein wenig
...);-)
IV. Empfangen von Nachrichten
Wenn der Computer eine neue Nachricht zugeschickt bekommt, so
wird dies in einem Ereignis von der DXPlay-Komponente gemeldet und
zwar bei OnMessage. Dies ist der richtige Ansatzpunkt, um diese
Nachrichten weiter zu verarbeiten. Dazu sollte man sich mal den
Kopf dieses Ereignisses ansehen: PROCEDURE TForm1.DXPlayMessage(Sender: TObject; From: TDXPlayPlayer;
Data: Pointer; DataSize: Integer);
Das Sender Objekt sollte eigentlich klar sein; es handelt sich
nicht um einen Bestandteil von DXPlay, sondern ist bei jedem
Ereignis vorhanden. Interessanter wird es danach allerdings bei From
vom Typ TDXPlayPlayer. Diese Variable beinhaltet einige Information
über denjenigen, der die Nachricht abgeschickt hat, zu Beispiel eine
ID oder einen Namen. InData ist die eigentliche Nachricht, die
DXPlay empfangen hat, und in DataSize wurde gespeichert, wie groß
Data ist.
Bevor man diese Nachricht nun bearbeiten kann, muss man erst mal
ermitteln, um welchen Nachrichtentyp es sich hierbei eigentlich
handelt, um sie dann entsprechend zu behandeln. Es würde nämlich
fatale Folgen haben, wenn man nun statt einer Textnachricht
plötzlich ein Bitmap in die Hände bekommt und dieses dann wie eine
Nachricht behandelt ...
Deshalb wird erst mal abgefragt, um welchen Nachrichtentyp es
sich eigentlich handelt: IF DXPlayMessageType(Data) = DX_ChatMessage THEN
Mit anderen Worten: "Wenn der Typ der Nachricht, die gerade in
Data geschickt worden ist, vom Typ DX_ChatMessage ist,
dann..." Deshalb ist es auch so wichtig, dass bei einer Nachricht
mindestens immer dwType enthalten seinmuss, ansonsten kann diese
Abfrage nicht funktionieren. Darunter könnte man nun zum Beispiel
auch auf andere Typen von Nachrichten reagieren und diese
entsprechend behandeln. Wir bleiben allerdingserst mal bei unserer
Textnachricht. Da wir nun wissen, um welchen Typ es sich bei dieser
Nachricht handelt, können wir nun auch aus ihr lesen, müssen
allerdings bedenken,dass es sich hierbei um einen Pointer, also
einen Zeiger im Speicher, handelt, die immer mit einem ^angesprochen
werden müssen. Am leichtesten ist es deshalb sowieso, mit WITH-DO zu
arbeiten: WITH TDXChatMessage(Data^) DO
So spart man sich das "Dächlein" bei jedem Kommando und hat auch
gleich die Möglichkeit, direkt auf die Variablen zuzugreifen.
Eigentlich ist nun schon alles fertig und man benötigtDXPlay nicht
mehr, sondern arbeitet nur noch mit dem"normalen" Delphi weiter, da
das Umformen jedoch nicht unbedingt leicht ist, will ich es hier
auch noch mal kurz ansprechen. Nun sollte manerst mal einen
einfachen String anlegen, in dem die Textnachricht wieder"lesbar"
gemacht werden soll, um die Nachricht dann wieder auszugeben. Nehmen
wir an,dass unsere String-Variable kreativer Weise S heißt. Als
erstes müssen wir angeben wie lang S ist. Wir benutzen dazu einfach
die Angaben die in Len stehen. SetLength(S,Len);
Schon hat S exakt die Länge der empfangenen Textnachricht.
Nun kopieren wir den Inhalt von Msg in S, da es sich bei Msg um
eine Kette von Chars (auch pChar genannt) handelt,muss wieder eine
Typ-Umwandlung erfolgen: StrLCopy(pChar(S) , Msg , Length(S));
Als erstes wird das Ziel angegeben, das ein pChar sein muss
(einfach pChar schreiben und es wird passend in den String kopiert),
dann wird die Quelle angegeben, in unserem Fall Msg, und dann noch
die Länge von S, die man ja vorher schon eingelesen hat, man könnte
natürlich auch hier Len nehmen ... Fertig ist das Ganze! Die
Nachricht ist nun in dem String S und lässt sich beliebig weiter
verwenden, z.B. in einem TMemo anzeigen.
Das ist der ganze Zauber um DXPlay! Ist doch eigentlich nicht so
kompliziert, oder? Wer immer noch verunsichert ist, der wird dann
spätestens beimBeispiel-Source-Code alles verstehen. Aber man will
ja schließlich nicht nur Nachrichten empfangen können, sondern auch
die Möglichkeit haben, dem Menschen am anderen Rechner zu antworten.
Das Verschicken funktioniert praktisch genauso; nur
andersherum.;-)
V. "Und ab die Post"
Wann eine Nachricht abgeschickt werden soll, bestimmt der
Programmierer selbst. Es kommt dabei ganz auf die Art des Programms
an. Bei einem Spiel ist es vielleicht notwendig, jede Sekunde ein
Paket an seinen Mitspieler zu schicken, während bei einem Chat
wirklich nur dann etwas geschickt werden soll, wenn der User etwas
sagen will. Deshalb empfiehlt es sich, für unser Programm einen
TButton auf das Formular zu setzen. Immer wenn der User eine
Nachricht senden will, drückt er einmal auf diesen Knopf und ab geht
die Post! Doch wie soll der Anwender etwas sagen können?! Ganze
einfach: Wir setzen eineTEdit-Komponente daneben, deren Inhalt jedes
Mal verschickt werden soll, wenn der User auf den Knopf drückt. Wir
nennen diese Komponente einfach Message_Edit. Bis jetzt sollte das
eigentlich jeder kennen ...;-)
Zum eigentlichen Verschicken brauchen wir zwei Variablen: Zum
einen einen Pointer vom Typ der Nachricht, die wir verschicken
wollen (in diesem Fall TDXChatMessage), und eine andere Variable in
der die Größe der Nachricht gespeichert wird. Am besten vereinbart
man diese Variablen als lokale, da man sie nach dem Senden nicht
mehr braucht: PROCEDURE TForm1.Send_ButtonClick(Sender: TObject);
VAR
ChatMsg: ^TDXChatMessage;
MsgSize: Integer;
Danach muss man erst mal ermitteln wie groß unsere zu sendende
Nachricht ist; dies geschieht wie folgt: MsgSize:=SizeOF(TDXChatMessage)+Length(Message_Edit.Text);
"Die Größe ist gleich der Größe des Nachrichttypes + der Länge
der Textnachricht!" Erinnern Sie sich noch, dass wir
TDXChatMessage.Msg mit einer Länge von 0 Zeichen definiert haben?
Dies darf hier nun auf gar keinen Fall vergessen werden, sondernmuss
noch zusätzlich zu der normalen Größe addiert werden. Dann braucht
man nur noch wissen,dass 1 Zeichen exakt 1 Byte groß ist und es
deshalb addiert die Größe des benötigten Speichers ergibt. Wenn Sie
zum Beispiel einen anderen Nachrichtentyp verwenden, der nur eine
Zahl verschickt, dann brauchen sie nichts mehr dazu zu addieren,
sondern es reicht, mitSizeOF(NachrichtenTyp) zu arbeiten. (Beispiel
ist im Source-Code!)
Unser ChatMsg zeigt derzeit noch auf gar nichts und hat noch
keine richtige Größe; dies sollte schnell behoben werden indem wir
Speicher in der passenden Größe reservieren: GetMem(ChatMsg,MsgSize);
Danach steht uns ChatMsg voll zur Verfügung und wir können damit
arbeiten; wieder nicht zu vergessen ist,dass es sich hierbei um
einen Pointer handelt und diese mit dem^-Zeichen angesprochen werden
müssen. Besser ist es deshalb wieder mit WITH-DO zu arbeiten.
Danach vergeben wir nur noch die Wert anChatMsg: dwType : = DX_ChatMessage;
Len : = Length(Message_Edit.Text);
StrLCopy(Msg,pChar(Message_Edit.Text),Length(Message_Edit.Text));
In dwType geben wir an, um was für einen Nachrichtentyp es sich
handeln soll; in unserem Fall um eine Textnachricht. Dies ist
wichtig, um beim Empfangen den Typ der Nachricht wieder richtig zu
bestimmen. Dann ermitteln wir wie lang die Textnachricht ist, mit
der FunktionLength, die eigentlich auch jeder kennen
sollte. Anschließend muss man nur noch die Textnachricht in Msg
reinquetschen. Dies geschieht mitStrLCopy. Als erstes das Ziel
angeben, dann die Quelle (die vorher mit pChar( ) umgewandelt
werdenmuss!) und dann noch die Länge der Nachricht. Die
Informationen sind nun alle in ChatMsg gespeichert und müssen nur
nach abgeschickt werden. Dies ist allerdings wieder sehr leicht: DXPlay.SendMessage(DPID_AllPlayers,ChatMsg,MsgSize);
So schickt man die Nachricht los. DPID_AllPlayers schickt an alle
Teilnehmer im Netz diese Nachricht, man könnte hier zum Beispiel
auch die ID eines einzelnen Computers angeben oder die einer Gruppe.
Dann folgt die Nachricht, die wir vorher erstellt haben und geben
dann noch ihre Größe mit auf den Weg.
Schlussendlich sollten Sie nicht vergessen, den Speicher, den Sie
mit GetMem reserviert haben, auch wieder freizugeben: FreeMem(ChatMsg);
Ein guter Stil wäre es dann natürlich, beim Beenden des Programms
DirectPlay wieder freizugeben mitDXPlay.Close, damit es nicht zu
Konflikten beim nächsten Start des Programms kommt.
VI. Zu Guter letzt!
So, das war es dann schon. Wenn Sie das alles verstanden haben,
dann können Sie nun mit DXPlay Nachrichtendurchs Netz schicken! Das
komplizierteste an diesen Beispielen ist eigentlich die
Typ-Umwandlung; der Rest sollte eigentlich kein Problem darstellen!
Wenn Sie sich dann noch denSource-Code des Beispielprogramms
ansehen, sollte es wirklich keine Probleme mehr geben. Hier werden
neben unseren Textnachrichten auch noch andere verschickt. Man kann
daraus sehr leicht erkennen, wie das Prinzip der Nachrichten
funktioniert. Außerdem reagiert das Programm auch noch darauf,dass
ein neuer Spieler das Netz betreten oder verlassen hat. Dies sollte
aber wirklich leicht verständlich sein undlässt sich demSource-Code
entnehmen. Falls es doch Probleme geben sollte, dann schreiben
Sie eine eMail an Phobeus@T-Online.de und ich
werde gerne versuchen, Ihnen zu helfen. Vielleicht gibt es dann
demnächst ein weiteres Tutorial, das sich speziell mit dem
Synchronisieren von Spielen beschäftigt. Auch überKritiken,
Anregungen oder Wünsche würde ich mich sehr freuen!
<<Vorheriges
Kapitel>> <<Nächstes
Kapitel>>
|