0

In my project I am trying to get data from a byte array. I am using structures to store the data. I am now doing the following:

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

I am getting a byte array through UDP. This byte array has a certain struct layout found here: https://forums.codemasters.com/topic/50942-f1-2020-udp-specification/

This is an example of the byte array send through udp:

E4-07-01-10-01-06-D2-60-7A-29-BA-53-CE-8C-73-0F-96-42-85-0E-00-00-00-FF-00-00-80-00-00-3F-C0-60-A8-3C-80-00-00-3F-32-02-98-12-00-00-20-00-20-00-20-00-20-00-64-64-64-64-64-64-64-64-5A-00-28-87-AF-41-28-87-AF-41-2B-C6-BB-41-2B-C6-BB-41-07-07-07-07-00-00-00-00-00-00-00-00-00-80-00-00-00-00-00-00-CC-10-00-00-20-00-20-00-20-00-20-00-55-55-5A-5A-55-55-5A-5A-5A-00-00-00-AC-41-00-00-AC-41-00-00-B8-41-00-00-B8-41-00-00-00-00-00-00-00-00-00-00-00-00-00-80-00-00-00-00-00-00-CC-10-00-00-20-00-20-00-20-00-20-00-55-55-5A-5A-55-55-5A-5A-5A-00-00-00-AC-41-00-00-AC-41-00-00-B8-41-00-00-B8-41-00-00-00-00-00-00-00-00-00-00-00-00-00-80-00-00-00-00-00-00-CC-10-01-00-20-00-20-00-20-00-20-00-55-55-5A-5A-55-55-5A-5A-5A-00-00-00-AC-41-00-00-AC-41-00-00-B8-41-00-00-B8-41-00-00-00-00-00-00-00-00-00-00-00-00-00-80-00-00-00-00-00-00-CC-10-00-00-20-00-20-00-20-00-20-00-55-55-5A-5A-55-55-5A-5A-5A-00-00-00-AC-41-00-00-AC-41-00-00-B8-41-00-00-B8-41-00-00-00-00-00-00-00-00-00-00-00-00-00-80-00-00-00-00-00-00-AB-0D-00-00-20-00-20-00-20-00-20-00-55-55-5A-5A-55-55-5A-5A-5A-00-00-00-AC-41-00-00-AC-41-00-00-B8-41-00-00-B8-41-00-00-00-00-00-00-00-00-00-00-00-00-00-80-00-00-00-00-00-00-D7-0E-00-00-20-00-20-00-20-00-20-00-55-55-5A-5A-55-55-5A-5A-5A-00-00-00-AC-41-00-00-AC-41-00-00-B8-41-00-00-B8-41-00-00-00-00-00-00-00-00-00-00-00-00-00-80-00-00-00-00-00-00-D7-0E-00-00-20-00-20-00-20-00-20-00-55-55-5A-5A-55-55-5A-5A-5A-00-00-00-AC-41-00-00-AC-41-00-00-B8-41-00-00-B8-41-00-00-00-00-00-00-00-00-00-00-00-00-00-80-00-00-00-00-00-00-AB-0D-00-00-20-00-20-00-20-00-20-00-55-55-5A-5A-55-55-5A-5A-5A-00-00-00-AC-41-00-00-AC-41-00-00-B8-41-00-00-B8-41-00-00-00-00-00-00-00-00-00-00-00-00-00-80-00-00-00-00-00-00-CC-10-00-00-20-00-20-00-20-00-20-00-55-55-5A-5A-55-55-5A-5A-5A-00-00-00-AC-41-00-00-AC-41-00-00-B8-41-00-00-B8-41-00-00-00-00-00-00-00-00-00-00-00-00-00-80-00-00-00-00-00-00-CC-10-00-00-20-00-20-00-20-00-20-00-55-55-5A-5A-55-55-5A-5A-5A-00-00-00-AC-41-00-00-AC-41-00-00-B8-41-00-00-B8-41-00-00-00-00-00-00-00-00-00-00-00-00-00-80-00-00-00-00-00-00-CC-10-00-00-20-00-20-00-20-00-20-00-55-55-5A-5A-55-55-5A-5A-5A-00-00-00-AC-41-00-00-AC-41-00-00-B8-41-00-00-B8-41-00-00-00-00-00-00-00-00-00-00-00-00-00-80-00-00-00-00-00-00-CC-10-00-00-20-00-20-00-20-00-20-00-55-55-5A-5A-55-55-5A-5A-5A-00-00-00-AC-41-00-00-AC-41-00-00-B8-41-00-00-B8-41-00-00-00-00-00-00-00-00-00-00-00-00-00-80-00-00-00-00-00-00-CC-10-00-00-20-00-20-00-20-00-20-00-55-55-5A-5A-55-55-5A-5A-5A-00-00-00-AC-41-00-00-AC-41-00-00-B8-41-00-00-B8-41-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-FF-00

I've setup the structs, and I get the correct packet header information. But when I try to get information from a packet itself, I get an exception.

To first get the packet header I do the following:

ReadOnlySpan<byte> remaining = received;
var header = MemoryMarshal.Cast<byte, PacketHeader>(remaining)[0];
remaining = remaining.Slice(Unsafe.SizeOf<PacketHeader>());

Now under "header" the correct packet headers are stored according to the byte array. I also get the right game version and all the other information stored in the packet header.

To get the following information out of the packet "CarTelemetry" I do the following:

switch (header.m_packetId)
            {
                case PacketType.CarTelemetry:
                int index = 0;
                    
                    foreach (var telemetry in MemoryMarshal.Cast<byte, CarTelemetryData>(remaining).Slice(0, 21))
                {
                    if(header.m_playerCarIndex == index)
                    {
                        Console.WriteLine($"speed: {telemetry.m_speed}, throttle: {telemetry.m_throttle}\n");
                        var temps = telemetry.m_tyresInnerTemperature;
                        Console.WriteLine($"type temps: {temps.A}/{temps.B}/{temps.C}/{temps.D}\n");
                    }

                    index++;
                }

                remaining = remaining.Slice(21 * Unsafe.SizeOf<CarTelemetryData>());
                var buttons = MemoryMarshal.Cast<byte, uint>(remaining)[0];
                Console.WriteLine($"Buttons: {buttons}\n");
                remaining = remaining.Slice(sizeof(uint));
                Console.WriteLine($"Unaccounted for: {remaining.Length}\n");
                break;
            }

More on this here: Byte Array to Struct UDP Packet

But with this code now I get the following exception: "System.ArgumentOutOfRangeException" The exception tells me that the parameter start is out of range of valid values, Parameter name: start.

I don't know what is going wrong and how to fix it.

Edit: As requested, I get the exception at the following line:

foreach (var telemetry in MemoryMarshal.Cast<byte, CarTelemetryData>(remaining).Slice(0, 20))

My structs look like this:

    public enum PacketType : byte
    {
        Motion = 0, // Contains all motion data for player’s car – only sent while player is in control

        Session = 1,// Data about the session – track, time left

        LapData = 2,//  Data about all the lap times of cars in the session

        Event = 3, // Various notable events that happen during a session

        Participants = 4, // List of participants in the session, mostly relevant for multiplayer

        CarSetups = 5, // Packet detailing car setups for cars in the race

        CarTelemetry = 6,  // Telemetry data for all cars

        CarStatus = 7 //  Status data for all cars such as damage
    }

    [StructLayout(LayoutKind.Explicit, Pack = 0, Size = 8)]
    public struct UshortQuad
    {
        [FieldOffset(0)]
        public ushort A;
        [FieldOffset(2)]
        public ushort B;
        [FieldOffset(4)]
        public ushort C;
        [FieldOffset(6)]
        public ushort D;
    }

    [StructLayout(LayoutKind.Explicit, Pack = 0, Size = 16)]
    public struct FloatQuad
    {
        [FieldOffset(0)]
        public float A;
        [FieldOffset(4)]
        public float B;
        [FieldOffset(8)]
        public float C;
        [FieldOffset(12)]
        public float D;
    }

    [StructLayout(LayoutKind.Explicit, Pack = 0, Size = 4)]
    public struct ByteQuad
    {
        [FieldOffset(0)]
        public byte A;
        [FieldOffset(1)]
        public byte B;
        [FieldOffset(2)]
        public byte C;
        [FieldOffset(3)]
        public byte D;
    }

    [StructLayout(LayoutKind.Explicit, Pack = 0, Size = 24)]
    public struct PacketHeader
    {
        [FieldOffset(0)]
        public ushort m_packetFormat;             // 2020
        [FieldOffset(2)]
        public byte m_gameMajorVersion;         // Game major version - "X.00"
        [FieldOffset(3)]
        public byte m_gameMinorVersion;         // Game minor version - "1.XX"
        [FieldOffset(4)]
        public byte m_packetVersion;            // Version of this packet type, all start from 1
        [FieldOffset(5)]
        public PacketType m_packetId;                 // Identifier for the packet type, see below
        [FieldOffset(6)]
        public ulong m_sessionUID;               // Unique identifier for the session
        [FieldOffset(14)]
        public float m_sessionTime;              // Session timestamp
        [FieldOffset(18)]
        public uint m_frameIdentifier;          // Identifier for the frame the data was retrieved on
        [FieldOffset(22)]
        public byte m_playerCarIndex;           // Index of player's car in the array
        [FieldOffset(23)]
        byte m_secondaryPlayerCarIndex;  // Index of secondary player's car in the array (splitscreen)
                                         // 255 if no second player
    };

    [StructLayout(LayoutKind.Explicit, Pack = 0, Size = 60)]
    public struct CarTelemetryData
    {
        [FieldOffset(0)]
        public ushort m_speed;                    // Speed of car in kilometres per hour
        [FieldOffset(2)]
        public float m_throttle;                 // Amount of throttle applied (0.0 to 1.0)
        [FieldOffset(6)]
        public float m_steer;                    // Steering (-1.0 (full lock left) to 1.0 (full lock right))
        [FieldOffset(10)]
        public float m_brake;                    // Amount of brake applied (0.0 to 1.0)
        [FieldOffset(14)]
        public byte m_clutch;                   // Amount of clutch applied (0 to 100)
        [FieldOffset(15)]
        public sbyte m_gear;                     // Gear selected (1-8, N=0, R=-1)
        [FieldOffset(16)]
        public ushort m_engineRPM;                // Engine RPM
        [FieldOffset(18)]
        public byte m_drs;                      // 0 = off, 1 = on
        [FieldOffset(19)]
        public byte m_revLightsPercent;         // Rev lights indicator (percentage)
        [FieldOffset(20)]
        public UshortQuad m_brakesTemperature;     // Brakes temperature (celsius)
        [FieldOffset(28)]
        public ByteQuad m_tyresSurfaceTemperature; // Tyres surface temperature (celsius)
        [FieldOffset(32)]
        public ByteQuad m_tyresInnerTemperature; // Tyres inner temperature (celsius)
        [FieldOffset(36)]
        public ushort m_engineTemperature;        // Engine temperature (celsius)
        [FieldOffset(40)]
        public FloatQuad tyresPressure;         // Tyres pressure (PSI)
        [FieldOffset(56)]
        public ByteQuad m_surfaceType;           // Driving surface, see appendice
    };
J.Meulenbeld
  • 185
  • 1
  • 17
  • 2
    On which line do you get an exception? Please [edit] and add your struct definition, and any nested structs – Charlieface Feb 22 '21 at 17:33
  • @Charlieface I've added the structures and gave the code which initiates the exception – J.Meulenbeld Feb 22 '21 at 18:17
  • The line that is throwing is doing multiple things. I suggest you break that into separate steps for now - for example, see if it is the "Slice" that throws. When you know what step is throwing: check all your assumptions on that call – Marc Gravell Feb 22 '21 at 18:43
  • But why are you slicing exactly 21 `CarTelemetryData` objects, you don't know how many there will be – Charlieface Feb 22 '21 at 18:53
  • @MarcGravell Thanks for the suggestion, I tested it a bit, and it works sometimes when I slice it with (0, 19) But the results are incorrect. I am not sure how this works, and how to separate the steps. Do you have any suggestions? (btw. don't know if you remember, but you have helped me exactly a year ago with this as well but then with the 2019 version of the game) :) – J.Meulenbeld Feb 22 '21 at 20:23
  • @Charlieface I know that there are 20 cars on the grid in the game, that is how I know the amount. I could be wrong about how to use the slice, but I know how much data is sent to me – J.Meulenbeld Feb 22 '21 at 20:24
  • Save each intermediate slice value to a variable and check it's contents in the debugger step-by-step. By the way `UInt16Quad.C` looks wrong, I think it should have offset 4. I suggest you go back check all your structs and offsets are declared correctly just to be sure. – Charlieface Feb 22 '21 at 20:32
  • @Charlieface Thanks for the suggestion! I've managed to figure out that it has something to do with the last slice:`var telemetry20 = MemoryMarshal.Cast(remaining).Slice(19, 1);` I am getting most values correctly now, still need to fix some issues with that. But with the slice it should have something to do with the last one... – J.Meulenbeld Feb 22 '21 at 20:54
  • I am really confused right now, I am now allowed to slice it like this: slice(0, 21) The first telemetry shows correct data, but the others not. Also in the code there is a piece that writes in the console the length (unaccounted piece) of the remaining data after slicing. When I let it print, It says that the amount unaccounted for is 19. Any clue on that? – J.Meulenbeld Feb 22 '21 at 22:12
  • P.S. I added the new and improved code/structures – J.Meulenbeld Feb 22 '21 at 22:19

0 Answers0