|
DirectPlay
Messaging |
|
Autor: Jack
Hoxley, 07/2000 |
Übersetzung: Sebastian
Bauersfeld, 11/2001 |
|
Ohne Zweifel kommen Internet-Spiele in den nächsten zwei Jahren groß raus (das hängt natürlich davon ab, wann du das hier gelesen
hast), und als
Spiel-Programmierer will man da natürlich mitmischen. Dies wird durch DirectPlay ohne große Umstände ermöglicht
Daten Senden |
Jetzt wird es interessant, die
Grund-Idee von DirectPlay ist, Informationen zwischen zwei Computern in Form von
Nachrichten auszutauschen.
Es gibt zwei Arten von Nachrichten, System-Nachrichten
(automatisch) und benutzer-definierte Nachrichten
(ausgelöst
von Spielern). Eine typische Systemnachricht ist
z.B. "Spieler XX ist dem Spiel beigetreten!" oder "Das Spiel wurde
beendet". Eine typisch benutzerdefinierte Nachricht kann eine
Chat-Nachricht (die der User sieht) oder ein
Update-Paket (vom
Programm gesendet) sein, dass z.B. Spieler X mitteilt, dass sich
Spieler Y bewegt hat - was dann auch auf dem Bildschirm
sichtbar wird. |
Wenn man Nachrichten
sendet, sollte man diese möglichst klein halten. Es ist klar,
dass große Nachrichten alles verlangsamen (denn der Empfänger
muss sie ja auch schnell downloaden).
Eine hohe Wartezeit (Latency, Ping/Lag) ist die Folge davon, wenn man viele Daten verschickt - jeder Online-Spieler wird dir sagen, dass das nicht gut fürs Spiel ist. Es gibt drei
Sachen, die man beachten sollte, wenn man Pakete
(Nachrichten)
sendet: |
1. |
Kompression. Dieses Thema fordert viel Testarbeit. Aber
bevor man große Datenmengen verschickt, sollte man versuchen,
sie erst mal zu komprimieren. Man sollte dabei aber beachten,
dass der Empfänger diese Daten erst wieder dekomprimieren
muss, bevor er diese nutzen kann.
|
2. |
Zeiteinteilung.
Viele Leute beachten das nicht, aber es steigert die
Geschwindigkeit des Programms enorm. Viele machen den Fehler,
ein Datenpaket in jedem Schleifendurchlauf zu verschicken,
wenn man einen schlechten Ping hat, dann wird dieser dadurch
nur noch verschlechtert. Zeiteinteilung heißt
z.B., dass man
Daten nur bei jeden 2., 3. oder 4. Schleifendurchlauf verschickt.
Bei einen guten PC würde das ausreichen, mit einem
schlechterem hat man da aber schon so seine Probleme. Hier
wäre es vielleicht besser, Datenpakete nur alle
100ms (10x pro
Sekunde) zu verschicken. Man kann in diesen Dingen auch den
User entscheiden lassen. Er wird schon wissen, wie gut seine
Verbindung ist. Er nimmt vielleicht weniger flüssige Bewegungen
für ein bisschen schnelleres Spielen in Kauf.
|
3. |
So wenig wie möglich senden. Man sollte nichts in das
Datenpaket packen was man auch nicht braucht. Wenn man eine
neue X, Y, Z-Koordinate für einen Spieler sendet, sollte man sie
nicht in der Form "X = 90 Y =
12 Z = 0.1" (15 Byte) sondern in der
Form "90|12|0.1" (9 Byte) senden. Somit hat man 6
Bytes gespart. Vorzugsweise kann man diese Informationen
als Binärzahlen verschicken. Das scheinen alles nur winzige
Kleinigkeiten zu sein, allerdings können sie großen Einfluss
auf das Spiel haben. |
|
|
Nun möchten
wir eine Nachricht senden
|
Selbstverständlich muss man zwei
Dinge beim Senden einer Nachricht angeben: die Quelle und das
Ziel. Als Erstes werden wir lernen, wie man eine Nachricht
beschreibt. Dafür gibt es einen eingebauten Datentypen, die
DirectPlayMessage. Diese füllen wir mit Informationen und
verschicken sie. |
Definieren wir ein paar
Konstanten, die die Art der Nachricht angeben, das ist wichtig,
da der Zielcomputer somit weiß welche Art von Nachricht er
bekommen hat (entweder er bekommt eine Chat-Nachricht oder er
empfängt wichtige Daten für das Spiel) |
|
|
const
MyGame_ChatMessage = 0 const
MyGame_DataPacket = 1 const
MyGame_SomeOtherType = 2 const
MyGame_YetAnotherMessageType = 3
'Dieses Objekt beinhaltet alles was wir zum
Senden brauchen. Dim Msg As
DirectPlayMessage
'Wir benutzen DirectPlay zum initialisieren
unseres
'Nachrichten-Objektes. Set Msg =
Dp.CreateMessage
'Die Nachricht erhält
unsere IdentifikationsKonstante.
Msg.WriteLong
MyGame_ChatMessage
'Nun fügen wir der Nachricht einen String hinzu
Msg.WriteString "Hallo. Ich bin ein Chat-String."
'Es gibt einige verschiedene Datentypen die man in die Nachricht
'hineinschreiben kann:
'WriteSingle = Daten vom Typ Single
'WriteDouble = Daten vom Typ Double
'WriteShort = Daten vom Typ Integer
'WriteLong = Daten vom Typ Long
'WriteGUID = Daten vom Typ String
'WriteString = Daten vom Typ String
'WriteByte = Daten vom Typ Byte
'Nun haben wir die Nachricht mit allen wichtigen Daten gefüllt und
'können sie zu einem oder 'mehreren Spielern senden
dp.SendEx PlayerID, DPID_ALLPLAYERS, DPSEND_GUARANTEED, Msg, 0, 0, 0
|
|
Nun müssen wir erst mal
ein paar Parameter von diesem Aufruf erklären:
|
Der erste Parameter ist die Spieler-ID des lokalen Computers. Diese ID wird zurückgegeben wenn man einen Spieler erstellt.
|
Der Zweite gibt an wer die Nachricht bekommen
soll (entweder
man gibt die spezifische ID für einen Spieler an, oder man
schickt die Nachricht mit DPID_ALLPLAYERS an alle Mitspieler)
|
Der dritte Parameter beschreibt wie die Nachricht gesendet werden
soll, hier ein paar Beispiele:
|
|
DPSEND_ASYNC
|
Das Programm versucht solange die Nachricht zu
senden bis dieser Versuch erfolgreich war
|
|
DPSEND_ENCRYPTED
|
Entschlüsselt die Nachricht; ist nur in
einer gesicherten Session möglich
|
|
DPSEND_NOSENDCOMPLETEMSG
|
Funktioniert nur wenn
ASYNC auch benutzt wird.
|
|
DPSEND_SIGNED
|
Wenn möglich sollte man eine digitale
Signatur benutzen. Funktioniert nur zusammen mit dem
GUARANTEED
flag.
|
|
GUARANTEED
|
Das Programm versucht solange die Nachricht
zu senden bis diese beim Empfänger angekommen ist.
|
Der vierte
Parameter definiert den Prioritätslevel; nicht jeder
Server / jede Verbindung unterstützt diese Option, darum sollte
man darauf achten ob ein DPERR_UNSUPPORTED erscheint. Die
Wichtigkeit der Nachricht kann eine Zahl zwischen
0 und 65536
sein, und wird nur gesendet, wenn es keine anderen
Nachrichten mehr gibt die eine höhere Priorität haben.
|
Der fünfte Parameter gibt das
TimeOut in ms an. Wenn eine Nachricht
nicht innerhalb der angegebenen Zeit gesendet werden kann,
wird sie gelöscht. Das ist nützlich wenn die Nachricht nur zu einen bestimmten Zeitpunkt wichtig ist. Wenn die Zeit auf 0
gesetzt wird, gibt es kein Zeitlimit. Diese Eigenschaft unterstützen auch nicht alle Server.
|
Der letzte Parameter kann meistens ignoriert werden.
Er ist nur von Bedeutung für die Nachrichten- ID.
|
|
So,
nun bist du in der Lage eine Nachricht zu einem anderen Spieler
zu senden. Du musst nur gut überlegen welche Nachrichten -Parameter
( Flags) du
benutzt, da sich das stark auf dein Programm auswirkt.
z.B.
brauchen nur Chat-Nachrichten den GUARANTEED-Flag, damit man auch
sicher gehen kann, dass die Nachricht beim Empfänger angekommen
ist. |
Ein
Datenpaket (das z.B. mitteilt, dass sich irgendein
Objekt bewegt hat) braucht den GUARANTEED-Flag
nicht. Anstelle
dessen kann man den ASYNC-Flag benutzen, da sich das Objekt ja
sicherlich kontinuierlich bewegt (und somit mehre Nachrichten
hintereinander gesendet werden müssen). Dadurch sind
vorhergehende Nachrichten schon wieder veraltet.
Nun haben wir etwas
gesendet und wollen jetzt etwas empfangen. |
Es macht keinen Sinn Nachrichten zu senden, wenn man sie nicht
empfangen kann. Also müssen wir unseren Code nun an die Ansprüche
der Nachrichten anpassen. Wenn man erst mal alle Daten von
DirectPlay empfangen hat, kann man damit machen was man will.
Dabei sollte man aber beachten, dass man die Daten in derselben
Reihenfolge aus der Nachricht auslesen muss, wie man sie
dort hineingeschrieben hat. |
|
|
'Nun brauchen wir erst mal ein paar Variablen...
Dim FromThisPlayer As
Long
Dim
ToThisPlayer As Long
Dim NumMessagesWaiting As Long
Dim MsgType As
Long
Dim Msg As
DirectPlayMessage
'Die Anzahl der anstehenden Nachrichten für unseren Spieler
'herausfinden.
NumMessagesWaiting = Dp.GetMessageCount(PlayerID)
Do While NumMessagesWaiting > 0
Set Msg = Dp.Recieve(FromThisPlayer, ToThisPlayer, DPRECIEVE_ALL)
'Die Variablen FromThisPlayer und ToThisPlayer haben die ID's
'der zu ihnen passenden Spieler.
'DPRECIEVE_ALL heißt grundsätzlich das wir nach allen
'Nachrichten gucken. Nun können wir alle Daten entpacken.
MsgType = Msg.ReadLong()
'Nun bearbeiten wir die Nachricht - das werden wir in einer
'anderen Prozedur tun.
ProcessMessage MsgType, FromThisPlayer
'Nun verringern wir die Anzahl der wartenden Nachrichten,
'weil wir ja gerade eine Nachricht gelesen haben.
NumMessagesWaiting = NumMessagesWaiting -1
Loop
Sub ProcessMessage(Type As Long, From As Long)
If From = DPID_SYSMSG then
'Wir haben eine Nachricht von DirectPlay erhalten. Es gibt
'eine Menge System-Nachrichten; die wichtigsten sind:
'DPSYS _CREATEPLAYERORGROUP - Ein neuer Spieler oder eine neue
'Gruppe sind beigetreten
'DPSYS _DESTROYPLAYERORGROUP - Ein existierender Spieler
'verlässt das Spiel
'DPSYS_HOST - Der Host verlässt das
Spiel, die Applikation die
'diese Nachricht empfängt ist nun der neue Host.
'DPSYS _SESSIONLOST - Die Verbindung mit der Session wurde
'verloren.
Else
'Wir haben eine
benutzerdefinierte Nachricht erhalten. Wir
'sollten Select Case benutzen:
Select Case Type
Case MyGame_ChatMessage
'Hier kommt der Code hinein, der eine Chat-Nachricht
'entpackt und auf dem Bildschirm abbildet
Case MyGame_DataPacket
'Hier kommt der Code hinein, der ein Datenpaket
entpackt
'und verarbeitet.
End Select
'Du solltest weitere Cases für die Konstanten die wir oben
'deklariert haben, einfügen.
End If
End Sub
|
|
|
Nun solltest
du in der Lage sein, Nachrichten für deine Anwendung zu
senden und zu empfangen. Einen ziemlich großen Teil von
DirectPlay haben wir schon geschafft. Das schlimmste hast du
also hinter dir.
|
Einen
Beispiel-Chat können Sie hier
downloaden.
|
|
|
Download
tip0185.zip
(13,3 kB) |
|
Downloads
bisher: [ 2460
] |
|
|