By: Jack Hoxley
Written: July 2000
You will need to have read the
tutorial on DirectPlay Basics before attempting
any of this.
This is where it gets
interesting. In theory the whole idea behind DirectPlay is to send information
(in the form of messages) between two computers. There are two types of messages
used in DirectPlay - System messages (Automatic) and User-Defined messages (Incurred
by the player doing something). A typical system message will be something along
the lines of "XX Player has joined" or "Game has terminated"
(In less useful words). A typical User-Defined message can be a chat message
(which the user sees) or an update packet - sent by the program; for example,
telling player X that player Y has moved - and to update it on their screen.
When sending packets around keep
them small. It should be fairly obvious that sending large packets will slow
things down (as the end-player has to effectively download them). A high latency
(Ping/Lag) is what is incurred when sending lots of data - any online player
will tell you that a high latency is bad. There are three main things you need
to bare in mind when sending packets:
- Compression. This would require
testing, but if you need to send lots of data around try compressing it first
- bare in mind that the player at the other end will need to de-compress it
before it can be used.
- Time Slicing. A lot of people
ignore this - but it will speed your game up dramatically. Most people make
the mistake of sending a packet on every cycle of the loop - if there is a
high latency you will only increase it this way. Time slicing involves sending
the data on every 2nd loop 3rd, 4th loop etc.. This way is fine for a fairly
fast machine, but if it is a slower machine then it will grind to a halt effectively.
So, you could also try sending data every 100ms - 10x a second; or less. You
could even let the user choose - he/she will know how good their connection
is, and may well want to sacrifice smooth movement for slightly faster gameplay.
- Minimal Data. Dont include
anything in the packet that isn't required - If you are sending a new X,Y,Z
coordinate for the player - DO NOT send it like "X=90 Y=12 Z =0.1"
(15 bytes); the same could be sent as "90|12|0.1" (9 bytes); thats
6 less bytes you have to send, preferably, send it in binary. Assuming XYZ
are all single's then to send any value for them it would be 12 bytes (even
if in string form it's 100 characters long). It may seem like peanuts to you now, but it
can make a big difference in the actual game...
So, I want to send a message
There are (obviously) two parts to sending a message - the source sending it
and the destination receiving it. First we'll learn how to compile a message.
There is a built in data type for this; "DirectPlayMessage", basically,
we fill this with valid information and then send this data.
|'We define ourselves some
constants to hold the message type - this is important because when
'the destination wants to unpack the data it can use this value to work
out what it should be looking for.
const MyGame_ChatMessage = 0
const MyGame_DataPacket = 1
const MyGame_SomeOtherType = 2
const MyGame_YetAnotherMessageType = 3
'This object holds everything that we need to send.
Dim Msg As DirectPlayMessage
'Use DirectPlay to initialise our message object.
Set Msg = Dp.CreateMessage
Msg.WriteLong MyGame_ChatMessage 'The message will
hold an identifier
Msg.WriteString "Hello. I am a chat string." 'Stick
the string data here
'There are several different data types you can
write into the message; and you can use more than one:
'WriteSingle = Any valid single data type (ie. Decimal number)
'WriteDouble = Any valid Double data type (Same as a single, but bigger)
'WriteShort = Any valid Integer data type
'WriteLong = Any valid Long data type (Same as integer, but bigger)
'WriteGUID = Writes a string to the message - Almost identical to the
'WriteString = Write any string to the message, this can be used to send
'WriteByte = Writes a byte to the message
'Now that we've compiled our message into a single structure; we can send
it to a single player,
' or lots of players.
dp.SendEx PlayerID, DPID_ALLPLAYERS, DPSEND_GUARANTEED, Msg, 0, 0, 0 .
'There are lots of flags to go in here. The first
flag MUST be the player ID of a player on the loal computer.
'This value should be the value returned when you created a player. The
second flag is who it is going to
'Get the message; you can use the constant DPID_ALLPLAYERS otherwise specify
a player number
'which corresponds with the number they got when they created their player.
The third flag describes
'How it should be sent. The flags that can be used are listed below:
'DPSEND_ASYNC = The program keeps on going straight away; and does not
pause until the message is sent
'DPSEND_ENCRYPTED = Encrypts the message; only available in a secure session
'DPSEND_GUARANTEED = If possible; the message is garaunteed to get there
'DPSEND_NOSENDCOMPLETEMSG = This can only go in if ASYNC is posted as
'DPSEND_SIGNED = Where possible use a digital signiture. It can only go
with the GUARANTEED flag.
'The fourth flag defines the priority level of the message; not every
server/connection will support this option;
' so be wary of DPERR_UNSUPPORTED being generated. It is on a scale of
0-65536 and it will only
' be sent when there are no messages with higher priority waiting.
'The fifth flag is the timeout in Ms, again, not all servers support this
option. If the message can not be sent within the
'specified time limit it is cancelled. This is useful if a message is
only valid for a short amount of time. Setting to 0 means
'No time limit.
'The last parameter can be ignored most of the time; it forces DP to append
this value to the message ID.
Okay. Now you should be able
to send a message to another player. You need to decide what type of message
flags you need to use; otherwise it may well affect your gameplay. For example,
Only Chat messages really need to be sent GUARANTEED as you want to make sure
that it gets to the destination. An update packet (ie. Unit XX has moved) doesn't
need to be sent GUARANTEED; it can be sent ASYNC instead. This is because the
unit is quite likely to move again (and send another message) so the previous
message may well be out of date or replaced very quickly.
Now I've sent something; I
want to recieve something.
There is no point in sending a message If you cant recieve it - fairly obvious
really. You will need to modify this code to suit your message needs - once
you've recieved the data from DirectPlay it's up to you to do something to it.
Note; you must take out the data in the same order that you packed it.
|'We need a few variables
to hold stuff....
Dim FromThisPlayer as long, ToThisPlayer as long, NumMessagesWaiting as
long, MsgType as Long
Dim Msg as DirectPlayMessage
NumMessagesWaiting = Dp.GetMessageCount(PlayerID)
'check the messages for our Player ONLY
Do While NumMessagesWaiting >0
Set Msg = Dp.Recieve(FromThisPlayer, ToThisPlayer, DPRECIEVE_ALL)
'The variables FromThisPlayer and ToThisPlayer have
the ID numbers of their respective players.
'DPRECIEVE_ALL basically means that we look at all the messages. We can
now unpack the data
MsgType = Msg.ReadLong()
'Now we process the message - we'll do this in another
ProcessMessage MsgType, FromThisPlayer
'Now we decrement the number of messages waiting -
to clear the message we just read.
NumMessagesWaiting = NumMessagesWaiting -1
Sub ProcessMessage(Type as Long, From as Long)
If From = DPID_SYSMSG then
'We have a message from the DirectPlay system. There
are a lot of System Messages; the most important ones
'DPSYS _CREATEPLAYERORGROUP - A new player or group have joined
'DPSYS _DESTROYPLAYERORGROUP - An existing player has left
'DPSYS_HOST - The current host has left the game; the application that recieves
this is now the new host.
'DPSYS _SESSIONLOST - The connection with the session has been lost.
'We Have a message of custom-Type. The best method
is to use a Select Case:
Select Case Type
'Execute code that unpacks and displays a chat message
'Do Code that unpacks a data packet, and acts on it.
End Select 'Just keep on adding Case's for the constants
You should now be able to read
and write messages for your applications. This is a rather large part of DirectPlay
- so now it is all down hill from here; you've done the hard stuff....