Multiplayer
BlitzMax Forums/BlitzMax Programming/Multiplayer
| ||
I'm just curious as to "what" messages are sent to and from clients and servers in order to sync information. Do you use WriteLine() and specify some sort of information details like (string)"object1.x=13" then have an interpreter that breaks down the string and assigns the details properly?! Or is their typically a way to automatically sync data? |
| ||
Usually you have to define your own package structure to fit your application. You basically need an ID to identify the package and maybe the sender. For that you can use 2 bytes: WriteByte(ClientID) WriteByte(PackageID) Then, depending on the type of package (updating player position, updating name, etc.) you set up the rest of the package. Ex: WriteFloat(Player.x) WriteFloat(Player.y) On the other side, you interpret the package: ClientID = ReadByte() 'so you know the sender PackageID = ReadByte() Select (PackageID) ... Case PID_UPD_POSITION NewX = ReadFloat() NewY = ReadFloat() ... End Select |
| ||
I find it best to put a whole packet together and send it at once. Rather than writing bits at a time. If you are using UDP and you send the packet bits at a time and on small portion of it doesn't arrive or arrives out of order, then you will have a mess to deal with. If you put a whole packet together and send it, then if it doesn't arrive, you don't have the confusion of only receiving parts of the packet. Of course, if you use TCP, then this won't matter. But, TCP is slower than UDP. |
| ||
Raknet takes a bit of getting used to, but for a serious multiplayer game it cannot be beaten. It is fully-featured and faster than any native Blitz multiplayer code I ever wrote. For a simple game, stick with TaskMaster's suggestion. It might be a good idea to wrap the data you send (usually called packets) into types. Strict Type TPacket Abstract Const id_position:Byte = 1 Function from_stream:TPacket(stream:TStream) Local id = stream.ReadByte() Select id Case id_position Local playerid = stream.ReadByte() Local x = stream.ReadInt() Local y = stream.ReadInt() Return TPositionPacket.Create(playerid, x, y) 'case id_another 'case id_yet_another Default DebugLog "Unknown packet detected of type " + id Return Null End Select End Function Method to_stream(stream:TStream) Abstract Method get_id:Byte() Abstract Method ToString:String() Abstract End Type Type TPositionPacket Extends TPacket Field playerid, x, y Method get_id:Byte() Return id_position End Method Method to_stream(stream:TStream) stream.WriteByte(get_id()) stream.WriteByte(playerid) stream.WriteInt(x) stream.WriteInt(y) End Method Method ToString:String() Return "TPositionPacket playerid=" + playerid + " x=" + x + " y=" + y End Method Function Create:TPositionPacket(playerid, x, y) Local position:TPositionPacket = New TPositionPacket position.playerid = playerid position.x = x position.y = y Return position End Function End Type It's VERY important you read data out in the exact same order you write it in. It's also very important you read all the required data, and the right types, and don't overread. Or it will screw up the next packet you read. That's why I store the read data in locals instead of putting reads directly as function parameters. You're not guaranteed they will be in the same order that way. It's up to you to check if the stream is empty or not before passing to the function. |