J.Meulenbeld
J.Meulenbeld

Reputation: 355

F1 2019 UDP decoding

I am currently working on my own display for an F1 steering wheel. F1 2019 (by codemasters) sends data through UDP. This data is stored in a byte array.

I am having some issues decoding the array being returned. The problem is that I get a lot of information back, but I don't know what to do with it. I will run you through what I have tried.

I am connecting to the game through port 20777 (standard port for the game):

using System.Net;
using System.Net.Sockets;

var Client = new UdpClient(20777); //Connectionport

In this next piece of code, I get the information from the game:

var RemoteIP = new IPEndPoint(IPAddress.Any, 60240);

byte[] received = Client.EndReceive(res, ref RemoteIP);

As you can see, the data from the game currently gets stored in a byte array.

Here comes the hard part (for me).

The data F1 2019 sends is packed in structs (from what I understand from their site). But I have no idea how I get the information out of the byte array and into the correct variables (e.g., what the current speed is, or in what gear the car is).

The information about the packets is on the website from codemasters:

https://forums.codemasters.com/topic/44592-f1-2019-udp-specification/

Now the actual question:

When I type this line of code:

short game_version = BitConverter.ToInt16(received, 0);

And I display it in a textbox, the variable game_version is now 2019.

I don't understand why, with the indexnumber 0, the bytes are converted in 2019.

And I don't know which index numbers to use to get each of the variables I want.

I hope someone can shine some light on this matter. On the codemasters forum everyone seems to know how to get the data from the byte array.

With kind regards.

Upvotes: 1

Views: 1681

Answers (1)

J.Meulenbeld
J.Meulenbeld

Reputation: 355

Okay so what I have found (Thanks to Jeremy) is that I need to get a structure out of the byte array. I followed these examples: Example1 Example2

So Codemasters (Makers of F1 2019) are sending a byte array with all the information needed. They have converted structs in to the byte array. These structs contain all the information which the game sends out. So the thing that needs to happen is converting the byte array back to structs. Keep in mind that the structlayout from Codemasters need to stay the same, Else you won't get the right information in the right variable.

PacketHeader packetheader = ByteArrayToPacketHeader(received);

PacketHeader ByteArrayToPacketHeader(byte[] bytes)
{
   GCHandle handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
   PacketHeader stuff;
   try
   {
     stuff = (PacketHeader)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(PacketHeader));
   }
   finally
   {
   handle.Free();
   }
   return stuff;
   }

Currently the code above converts the incoming byte array (received) into the struct PacketHeader. The struct looks like this:

public struct PacketHeader
    {
        public ushort m_packetFormat;         // 2019
        public byte m_gameMajorVersion;     // Game major version - "X.00"
        public byte m_gameMinorVersion;     // Game minor version - "1.XX"
        public byte m_packetVersion;        // Version of this packet type, all start from 1
        public byte m_packetId;             // Identifier for the packet type, see below
        public ulong m_sessionUID;           // Unique identifier for the session
        public float m_sessionTime;          // Session timestamp
        public uint m_frameIdentifier;      // Identifier for the frame the data was retrieved on
        public byte m_playerCarIndex;       // Index of player's car in the array
    };

Every Packet (struct) with information has a PacketHeader. By converting the Header of each Packet you get the corresponding information in the variable: stuff.

Here you can see the information of the PacketHeader while debugging

I'll try to explain what the code does. I am not sure that I can explain it 100% because I don't understand it fully myself.

PacketHeader ByteArrayToPacketHeader(byte[] bytes)
{

}

First I made a new method. This method will pull the struct out of the byte array. Then I added the needed variables to the method:

GCHandle handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
PacketHeader stuff;

I don't really understand the Handle but if it is what I think it is, it pins down the bytes so they can be converted correctly.

I also made a variable: stuff. In this variable the struct will be "stored". In the next part I added the conversion code:

try
{
  stuff = (PacketHeader)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(PacketHeader));
}
finally
{
   handle.Free();
}
return stuff;

The conversion is done is this code. When the PacketHeader is filled in, in the code, the struct layout will be followed and the variables will be filled.

The last part that needs to be done is calling this method. I did this like this:

PacketHeader packetheader = ByteArrayToPacketHeader(received);

Here you can see that I called the method PacketHeader and created a variable called packetheader. In this variable all the information will be stored.

Important! Fill in the byte array you want to use in the received slot. This way the method will use that byte array.

If I now want to call a variable out of the PacketHeader struct I simply write this:

packetheader.m_packetFormat (example)

Currently I am able to get the right information out of the PacketHeader but still not out of the other structs. But I am one step closer to completing my project!

I am sorry if the explanation is not entirely correct. (I am still learning myself). Please contact me if you have any tips or notes about this explanation. I would like to know more about this subject.

Whith kind regards.

Upvotes: 5

Related Questions