private int readRawData(byte[] rawData)
        {
            // unpack the packet header manually before attempting to load into a struct
            uint packetNumber                        = BitConverter.ToUInt32(rawData, 0);
            uint categoryPacketNumber                = BitConverter.ToUInt32(rawData, 4);
            int  partialPacketIndexBytes             = rawData[8];
            int  partialPacketNumber                 = rawData[9];
            EUDPStreamerPacketHandlerType packetType = (EUDPStreamerPacketHandlerType)rawData[10];
            int packetVersion                        = rawData[11];

            int frameLength = 0;

            switch (packetType)
            {
            case EUDPStreamerPacketHandlerType.eCarPhysics:
                telemPacketCount++;
                if (telemPacketCount > packetCountAtStartOfNextRateCheck)
                {
                    lastPacketRateEstimate = (int)((float)TimeSpan.TicksPerSecond * (float)(telemPacketCount - packetCountAtStartOfCurrentRateCheck) / (float)(DateTime.UtcNow.Ticks - ticksAtStartOfCurrentPacketRateCheck));
                    Console.WriteLine("Packet rate = " + lastPacketRateEstimate +
                                      "Hz, totals:" +
                                      "\rtelem = " + telemPacketCount +
                                      "\rraceDefinition = " + raceDefinitionPacketCount +
                                      "\rparticipants = " + participantsPacketCount +
                                      "\rtimings = " + timingsPacketCount +
                                      "\rgameState = " + gameStatePacketCount +
                                      "\rweather = " + weatherStatePacketCount +
                                      "\rvehicleNames = " + vehicleNamesPacketCount +
                                      "\rtimeStats = " + timeStatsPacketCount +
                                      "\rparticipantVehicleNames = " + participantVehicleNamesPacketCount +
                                      "\rin sequence = " + inSequenceTelemCount + " oos accepted = " + acceptedOutOfSequenceTelemCount + " oos rejected = " + discardedTelemCount);
                    packetCountAtStartOfCurrentRateCheck = telemPacketCount;
                    packetCountAtStartOfNextRateCheck    = packetCountAtStartOfCurrentRateCheck + packetRateCheckInterval;
                    ticksAtStartOfCurrentPacketRateCheck = DateTime.UtcNow.Ticks;
                }
                frameLength = UDPPacketSizes.telemetryPacketSize;
                Boolean sequenceCheckOK = isNextInSequence(packetNumber);
                if (sequenceCheckOK)
                {
                    inSequenceTelemCount++;
                }
                if (strictPacketOrdering && !sequenceCheckOK)
                {
                    discardedTelemCount++;
                }
                else
                {
                    GCHandle telemHandle = GCHandle.Alloc(rawData.Take(frameLength).ToArray(), GCHandleType.Pinned);
                    try
                    {
                        sTelemetryData telem = (sTelemetryData)Marshal.PtrToStructure(telemHandle.AddrOfPinnedObject(), typeof(sTelemetryData));
                        if (sequenceCheckOK || !telemIsOutOfSequence(telem))
                        {
                            buttonsState = ConvertBytesToBoolArray(telem.sDPad, telem.sJoyPad1, telem.sJoyPad2);
                            lastSequenceNumberForTelemPacket = (int)packetNumber;
                            workingGameState = StructHelper.MergeWithExistingState(workingGameState, telem);
                            newSpotterData   = true;
                        }
                    }
                    finally
                    {
                        telemHandle.Free();
                    }
                }
                break;

            case EUDPStreamerPacketHandlerType.eRaceDefinition:
                raceDefinitionPacketCount++;
                frameLength = UDPPacketSizes.raceDataPacketSize;
                GCHandle raceDefHandle = GCHandle.Alloc(rawData.Take(frameLength).ToArray(), GCHandleType.Pinned);
                try
                {
                    sRaceData raceDefinition = (sRaceData)Marshal.PtrToStructure(raceDefHandle.AddrOfPinnedObject(), typeof(sRaceData));
                    workingGameState = StructHelper.MergeWithExistingState(workingGameState, raceDefinition);
                }
                finally
                {
                    raceDefHandle.Free();
                }
                break;

            case EUDPStreamerPacketHandlerType.eParticipants:
                participantsPacketCount++;
                frameLength = UDPPacketSizes.participantsDataPacketSize;
                GCHandle participantsHandle = GCHandle.Alloc(rawData.Take(frameLength).ToArray(), GCHandleType.Pinned);
                try
                {
                    sParticipantsData participants = (sParticipantsData)Marshal.PtrToStructure(participantsHandle.AddrOfPinnedObject(), typeof(sParticipantsData));
                    workingGameState = StructHelper.MergeWithExistingState(workingGameState, participants);
                }
                finally
                {
                    participantsHandle.Free();
                }
                break;

            case EUDPStreamerPacketHandlerType.eTimings:
                timingsPacketCount++;
                frameLength = UDPPacketSizes.timingsDataPacketSize;
                GCHandle timingsHandle = GCHandle.Alloc(rawData.Take(frameLength).ToArray(), GCHandleType.Pinned);
                try
                {
                    sTimingsData timings = (sTimingsData)Marshal.PtrToStructure(timingsHandle.AddrOfPinnedObject(), typeof(sTimingsData));
                    workingGameState = StructHelper.MergeWithExistingState(workingGameState, timings);
                }
                finally
                {
                    timingsHandle.Free();
                }
                break;

            case EUDPStreamerPacketHandlerType.eGameState:
                gameStatePacketCount++;
                frameLength = UDPPacketSizes.gameStateDataPacketSize;
                GCHandle gameStateHandle = GCHandle.Alloc(rawData.Take(frameLength).ToArray(), GCHandleType.Pinned);
                try
                {
                    sGameStateData gameState = (sGameStateData)Marshal.PtrToStructure(gameStateHandle.AddrOfPinnedObject(), typeof(sGameStateData));
                    workingGameState = StructHelper.MergeWithExistingState(workingGameState, gameState);
                }
                finally
                {
                    gameStateHandle.Free();
                }
                break;

            case EUDPStreamerPacketHandlerType.eWeatherState:
                weatherStatePacketCount++;
                Console.WriteLine("Got an undocumented and unsupported weather packet");
                break;

            case EUDPStreamerPacketHandlerType.eVehicleNames:
                weatherStatePacketCount++;
                Console.WriteLine("Got an undocumented and unsupported vehicle names packet");
                break;

            case EUDPStreamerPacketHandlerType.eTimeStats:
                participantsPacketCount++;
                frameLength = UDPPacketSizes.timeStatsPacketSize;
                GCHandle timeStatsHandle = GCHandle.Alloc(rawData.Take(frameLength).ToArray(), GCHandleType.Pinned);
                try
                {
                    sTimeStatsData timeStatsData = (sTimeStatsData)Marshal.PtrToStructure(timeStatsHandle.AddrOfPinnedObject(), typeof(sTimeStatsData));
                    workingGameState = StructHelper.MergeWithExistingState(workingGameState, timeStatsData);
                }
                finally
                {
                    timeStatsHandle.Free();
                }
                break;

            case EUDPStreamerPacketHandlerType.eParticipantVehicleNames:
                participantsPacketCount++;
                frameLength = UDPPacketSizes.participantVehicleNamesPacketSize;
                GCHandle vehNamesHandle = GCHandle.Alloc(rawData.Take(frameLength).ToArray(), GCHandleType.Pinned);
                try
                {
                    sParticipantVehicleNamesData participantVehicleNames = (sParticipantVehicleNamesData)Marshal.PtrToStructure(vehNamesHandle.AddrOfPinnedObject(), typeof(sParticipantVehicleNamesData));
                    workingGameState = StructHelper.MergeWithExistingState(workingGameState, participantVehicleNames);
                }
                finally
                {
                    vehNamesHandle.Free();
                }
                break;
            }
            return(frameLength);
        }
Example #2
0
        public static pCars2APIStruct MergeWithExistingState(pCars2APIStruct existingState, sTimingsData timingsData)
        {
            existingState.mNumParticipants    = timingsData.sNumParticipants;
            existingState.mEventTimeRemaining = timingsData.sEventTimeRemaining;// time remaining, -1 for invalid time,  -1 - laps remaining in lap based races  --
            existingState.mSplitTimeAhead     = timingsData.sSplitTimeAhead;
            existingState.mSplitTimeBehind    = timingsData.sSplitTimeBehind;
            existingState.mSplitTime          = timingsData.sSplitTime; // what's this?
            if (existingState.mParticipantData == null)
            {
                existingState.mParticipantData = new pCars2APIParticipantStruct[32];
            }
            if (existingState.mRaceStates == null)
            {
                existingState.mRaceStates = new uint[32];
            }
            if (existingState.mLapsInvalidated == null)
            {
                existingState.mLapsInvalidated = new byte[32];
            }
            if (existingState.mPitModes == null)
            {
                existingState.mPitModes = new uint[32];
            }
            if (existingState.mCurrentSector1Times == null)
            {
                existingState.mCurrentSector1Times = new float[32];
            }
            if (existingState.mCurrentSector2Times == null)
            {
                existingState.mCurrentSector2Times = new float[32];
            }
            if (existingState.mCurrentSector3Times == null)
            {
                existingState.mCurrentSector3Times = new float[32];
            }
            for (int i = 0; i < existingState.mParticipantData.Length; i++)
            {
                sParticipantInfo newParticipantInfo = timingsData.sParticipants[i];
                Boolean          isHuman            = (newParticipantInfo.sCarIndex >> 7) == 1;
                uint             carIndex           = (uint)newParticipantInfo.sCarIndex & 127;

                Boolean isActive = (newParticipantInfo.sRacePosition >> 7) == 1;
                pCars2APIParticipantStruct existingPartInfo = existingState.mParticipantData[i];

                if (isActive)
                {
                    existingPartInfo.mIsActive = i < existingState.mNumParticipants;

                    existingPartInfo.mCurrentLap = newParticipantInfo.sCurrentLap;
                    // is this safe?:
                    existingPartInfo.mLapsCompleted      = existingPartInfo.mCurrentLap - 1;
                    existingPartInfo.mCurrentLapDistance = newParticipantInfo.sCurrentLapDistance;
                    existingPartInfo.mRacePosition       = (uint)newParticipantInfo.sRacePosition & 127;
                    existingPartInfo.mCurrentSector      = newParticipantInfo.sSector & 7;

                    // err... laps completed is missing?
                    // existingPartInfo.mLapsCompleted = (uint)newParticipantInfo.sLapsCompleted & 127;
                    byte lapInvalidated = (byte)(newParticipantInfo.sRaceState >> 7);
                    existingState.mRaceStates[i] = (uint)newParticipantInfo.sRaceState & 127;
                    if (i == existingState.mViewedParticipantIndex)
                    {
                        existingState.mRaceState = existingState.mRaceStates[i];
                    }
                    existingState.mLapsInvalidated[i]    = lapInvalidated;
                    existingState.mPitModes[i]           = (uint)newParticipantInfo.sPitModeSchedule & 28;
                    existingState.mPitSchedules[i]       = (uint)newParticipantInfo.sPitModeSchedule & 3;
                    existingState.mHighestFlagColours[i] = (uint)newParticipantInfo.sHighestFlag & 28;
                    existingState.mHighestFlagReasons[i] = (uint)newParticipantInfo.sHighestFlag & 3;
                    // no obvious slot in MMF for currentTime - do we need it if we have currentsectortime for S3?
                    if (existingPartInfo.mCurrentSector == 1)
                    {
                        existingState.mCurrentSector1Times[i] = newParticipantInfo.sCurrentSectorTime;
                    }
                    if (existingPartInfo.mCurrentSector == 2)
                    {
                        existingState.mCurrentSector2Times[i] = newParticipantInfo.sCurrentSectorTime;
                    }
                    if (existingPartInfo.mCurrentSector == 3)
                    {
                        existingState.mCurrentSector3Times[i] = newParticipantInfo.sCurrentSectorTime;
                    }

                    // and now the bit magic for the extra position precision...
                    float[] newWorldPositions = toFloatArray(newParticipantInfo.sWorldPosition, 1);
                    float   xAdjustment       = ((float)((uint)newParticipantInfo.sSector >> 6 & 3)) / 4f;
                    float   zAdjustment       = ((float)((uint)newParticipantInfo.sSector >> 4 & 3)) / 4f;

                    newWorldPositions[0]            = newWorldPositions[0] + xAdjustment;
                    newWorldPositions[2]            = newWorldPositions[2] + zAdjustment;
                    existingPartInfo.mWorldPosition = newWorldPositions;

                    if (i == existingState.mViewedParticipantIndex)
                    {
                        existingState.mLapInvalidated    = lapInvalidated == 1;
                        existingState.mHighestFlagColour = existingState.mHighestFlagColours[i];
                        existingState.mHighestFlagReason = existingState.mHighestFlagReasons[i];
                        existingState.mPitMode           = existingState.mPitModes[i];
                        existingState.mPitSchedule       = existingState.mPitSchedules[i];
                    }
                }
                else
                {
                    existingPartInfo.mWorldPosition = new float[] { 0, 0, 0 };
                    existingPartInfo.mIsActive      = false;
                }
                existingState.mParticipantData[i] = existingPartInfo;
            }
            return(existingState);
        }