public static pCars2APIStruct MergeWithExistingState(pCars2APIStruct existingState, sRaceData raceData) { Boolean isTimedSession = (raceData.sLapsTimeInEvent >> 15) == 1; int sessionLength = raceData.sLapsTimeInEvent / 2; // need the bottom bits of this short here - is it valid to just halve it? if (isTimedSession) { existingState.mLapsInEvent = (uint)sessionLength; existingState.mSessionLengthTimeFromGame = 0; } else { existingState.mSessionLengthTimeFromGame = 300 * (uint)sessionLength; // *300 because this is in 5 minutes blocks existingState.mLapsInEvent = 0; } existingState.mTrackLength = raceData.sTrackLength; existingState.mWorldFastestLapTime = raceData.sWorldFastestLapTime; existingState.mWorldFastestSector1Time = raceData.sWorldFastestSector1Time; existingState.mWorldFastestSector2Time = raceData.sWorldFastestSector2Time; existingState.mWorldFastestSector3Time = raceData.sWorldFastestSector3Time; existingState.mPersonalFastestLapTime = raceData.sPersonalFastestLapTime; existingState.mPersonalFastestSector1Time = raceData.sPersonalFastestSector1Time; existingState.mPersonalFastestSector2Time = raceData.sPersonalFastestSector2Time; existingState.mPersonalFastestSector3Time = raceData.sPersonalFastestSector3Time; existingState.mTrackLocation = raceData.sTrackLocation; existingState.mTrackVariation = raceData.sTrackVariation; existingState.mTranslatedTrackLocation = raceData.sTranslatedTrackLocation; existingState.mTranslatedTrackVariation = raceData.sTranslatedTrackVariation; existingState.mEnforcedPitStopLap = raceData.sEnforcedPitStopLap; return(existingState); }
public override Object ReadGameData(Boolean forSpotter) { CrewChiefV4.PCars2.PCars2SharedMemoryReader.PCars2StructWrapper structWrapper = new CrewChiefV4.PCars2.PCars2SharedMemoryReader.PCars2StructWrapper(); structWrapper.ticksWhenRead = DateTime.UtcNow.Ticks; lock (this) { if (!initialised) { if (!InitialiseInternal()) { throw new GameDataReadException("Failed to initialise UDP client"); } } previousGameState = StructHelper.Clone(currentGameState); currentGameState = StructHelper.Clone(workingGameState); if (forSpotter) { newSpotterData = false; } } structWrapper.data = currentGameState; if (!forSpotter && dumpToFile && dataToDump != null && currentGameState.mTrackLocation != null && currentGameState.mTrackLocation.Length > 0) { dataToDump.Add(structWrapper); } return(structWrapper); }
public static String getCarClassName(pCars2APIStruct shared, int participantIndex) { if (shared.mCarClassNames == null) { return(""); } return(getNameFromBytes(shared.mCarClassNames.Skip(participantIndex * 64).Take(64).ToArray())); }
public static pCars2APIStruct MergeWithExistingState(pCars2APIStruct existingState, sVehicleClassNamesData vehicleClassNamesData) { for (int i = 0; i < 60; i++) // why 60 class names here? Who knows { // we only have 20 bytes of data per name here // TODO: understand this data and map it } return(existingState); }
protected override float[] getWorldPositionOfDriverAtPosition(Object currentStateObj, int position) { pCars2APIStruct latestRawData = (pCars2APIStruct)currentStateObj; foreach (pCars2APIParticipantStruct participant in latestRawData.mParticipantData) { if (participant.mRacePosition == position) { return(new float[] { participant.mWorldPosition[0], participant.mWorldPosition[2] }); } } return(new float[] { 0, 0 }); }
public static pCars2APIStruct MergeWithExistingState(pCars2APIStruct existingState, sGameStateData gameStateData) { existingState.mGameState = (uint)gameStateData.mGameState & 7; existingState.mSessionState = (uint)gameStateData.mGameState >> 4; existingState.mAmbientTemperature = gameStateData.sAmbientTemperature; existingState.mTrackTemperature = gameStateData.sTrackTemperature; existingState.mRainDensity = gameStateData.sRainDensity; existingState.mSnowDensity = gameStateData.sSnowDensity; existingState.mWindSpeed = gameStateData.sWindSpeed; existingState.mWindDirectionX = gameStateData.sWindDirectionX; existingState.mWindDirectionY = gameStateData.sWindDirectionY; return(existingState); }
public override Object ReadGameData(Boolean forSpotter) { lock (this) { pCars2APIStruct _pcarsapistruct = new pCars2APIStruct(); if (!initialised) { if (!InitialiseInternal()) { throw new GameDataReadException("Failed to initialise shared memory"); } } try { int retries = -1; do { retries++; using (var sharedMemoryStreamView = memoryMappedFile.CreateViewStream()) { BinaryReader _SharedMemoryStream = new BinaryReader(sharedMemoryStreamView); sharedMemoryReadBuffer = _SharedMemoryStream.ReadBytes(sharedmemorysize); GCHandle handle = GCHandle.Alloc(sharedMemoryReadBuffer, GCHandleType.Pinned); try { _pcarsapistruct = (pCars2APIStruct)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(pCars2APIStruct)); } finally { handle.Free(); } } } while (_pcarsapistruct.mSequenceNumber % 2 != 0); tornFramesCount += retries; PCars2StructWrapper structWrapper = new PCars2StructWrapper(); structWrapper.ticksWhenRead = DateTime.UtcNow.Ticks; structWrapper.data = _pcarsapistruct; if (!forSpotter && dumpToFile && dataToDump != null && _pcarsapistruct.mTrackLocation != null && _pcarsapistruct.mTrackLocation.Length > 0) { dataToDump.Add(structWrapper); } return(structWrapper); } catch (Exception ex) { throw new GameDataReadException(ex.Message, ex); } } }
public static pCars2APIStruct MergeWithExistingState(pCars2APIStruct existingState, sParticipantsData participantsData) { if (existingState.mParticipantData == null) { existingState.mParticipantData = new pCars2APIParticipantStruct[32]; } int offset = (participantsData.mPartialPacketIndex - 1) * 16; // existingState is a struct, so any changes we make as we iterate this array will be done to a copy, not a reference for (int i = offset; i < offset + 16 && i < existingState.mParticipantData.Length; i++) { pCars2APIParticipantStruct existingParticipant = existingState.mParticipantData[i]; existingParticipant.mName = participantsData.sName[i - offset].nameByteArray; existingState.mParticipantData[i] = existingParticipant; } return(existingState); }
// For double-file manual rolling starts. Will only work when the cars are all nicely settled on the grid - preferably // when the game thinks the race has just started public override Tuple <GridSide, Dictionary <int, GridSide> > getGridSide(Object currentStateObj) { CrewChiefV4.PCars2.PCars2SharedMemoryReader.PCars2StructWrapper currentWrapper = (CrewChiefV4.PCars2.PCars2SharedMemoryReader.PCars2StructWrapper)currentStateObj; pCars2APIStruct latestRawData = currentWrapper.data; int playerIndex = PCars2GameStateMapper.getPlayerIndex(latestRawData); pCars2APIParticipantStruct playerData = latestRawData.mParticipantData[playerIndex]; float playerRotation = latestRawData.mOrientation[1]; if (playerRotation < 0) { playerRotation = (float)(2 * Math.PI) + playerRotation; } playerRotation = (float)(2 * Math.PI) - playerRotation; float playerXPosition = playerData.mWorldPosition[0]; float playerZPosition = playerData.mWorldPosition[2]; int playerStartingPosition = (int)playerData.mRacePosition; int numCars = latestRawData.mNumParticipants; return(getGridSideInternal(latestRawData, playerRotation, playerXPosition, playerZPosition, playerStartingPosition, numCars)); }
public static pCars2APIStruct MergeWithExistingState(pCars2APIStruct existingState, sParticipantVehicleNamesData participantVehicleNamesData) { int offset = (participantVehicleNamesData.mPartialPacketIndex - 1) * 16; if (existingState.mCarNames == null) { existingState.mCarNames = new byte[64 * 64]; } for (int i = 0; i < 16; i++) { ushort index = participantVehicleNamesData.sVehicles[i].sIndex; uint classIndex = participantVehicleNamesData.sVehicles[i].sClass; byte[] name = participantVehicleNamesData.sVehicles[i].sName; int start = (offset + i) * 64; int end = start + 64; int sourceIndex = 0; for (int j = start; j < end; j++) { existingState.mCarNames[j] = name[sourceIndex]; sourceIndex++; } } return(existingState); }
public static pCars2APIStruct MergeWithExistingState(pCars2APIStruct existingState, sTimeStatsData timeStatsData) { float[] lastSectorTimes = new float[32]; if (existingState.mFastestLapTimes == null) { existingState.mFastestLapTimes = new float[32]; } if (existingState.mFastestSector1Times == null) { existingState.mFastestSector1Times = new float[32]; } if (existingState.mFastestSector2Times == null) { existingState.mFastestSector2Times = new float[32]; } if (existingState.mFastestSector3Times == null) { existingState.mFastestSector3Times = new float[32]; } for (int i = 0; i < 32; i++) { sParticipantStatsInfo participantInfo = timeStatsData.sStats.sParticipants[i]; existingState.mFastestLapTimes[i] = participantInfo.sFastestLapTime; existingState.mFastestSector1Times[i] = participantInfo.sFastestSector1Time; existingState.mFastestSector2Times[i] = participantInfo.sFastestSector2Time; existingState.mFastestSector3Times[i] = participantInfo.sFastestSector3Time; existingState.mLastLapTime = participantInfo.sLastLapTime; lastSectorTimes[i] = participantInfo.sLastSectorTime; if (i == existingState.mViewedParticipantIndex) { existingState.mLastLapTime = participantInfo.sLastLapTime; } } existingState.lastSectorTimes = lastSectorTimes; return(existingState); }
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); }
public override void trigger(Object lastStateObj, Object currentStateObj, GameStateData currentGameState) { if (paused) { return; } CrewChiefV4.PCars2.PCars2SharedMemoryReader.PCars2StructWrapper currentWrapper = (CrewChiefV4.PCars2.PCars2SharedMemoryReader.PCars2StructWrapper)currentStateObj; pCars2APIStruct currentState = currentWrapper.data; ePitMode pitMode = (ePitMode)currentState.mPitMode; eGameState gameState = (eGameState)currentState.mGameState; // game state is 3 for paused, 5 for replay. No idea what 4 is... if (pitMode != ePitMode.PIT_MODE_NONE || gameState == eGameState.GAME_FRONT_END || (gameState == eGameState.GAME_INGAME_PAUSED && !CrewChief.Debugging) || gameState == eGameState.GAME_INGAME_REPLAY || gameState == eGameState.GAME_FRONT_END_REPLAY || gameState == eGameState.GAME_EXITED) { // don't ignore the paused game updates if we're in debug mode return; } CrewChiefV4.PCars2.PCars2SharedMemoryReader.PCars2StructWrapper previousWrapper = (CrewChiefV4.PCars2.PCars2SharedMemoryReader.PCars2StructWrapper)lastStateObj; pCars2APIStruct lastState = previousWrapper.data; DateTime now = new DateTime(currentWrapper.ticksWhenRead); float interval = (float)(((double)currentWrapper.ticksWhenRead - (double)previousWrapper.ticksWhenRead) / (double)TimeSpan.TicksPerSecond); if (currentState.mRaceState == (int)eRaceState.RACESTATE_RACING && lastState.mRaceState != (int)eRaceState.RACESTATE_RACING) { timeToStartSpotting = now.Add(TimeSpan.FromSeconds(timeAfterRaceStartToActivate)); } // this check looks a bit funky... whe we start a practice session, the raceState is not_started // until we cross the line for the first time. Which is retarded really. if (currentState.mRaceState == (int)eRaceState.RACESTATE_INVALID || now < timeToStartSpotting || (currentState.mSessionState == (int)eSessionState.SESSION_RACE && currentState.mRaceState == (int)eRaceState.RACESTATE_NOT_STARTED)) { return; } if (enabled && currentState.mNumParticipants > 1 && (enableSpotterInTimetrial || currentState.mSessionState != (uint)eSessionState.SESSION_TIME_ATTACK)) { int playerIndex = PCars2GameStateMapper.getPlayerIndex(currentState); if (playerIndex != currentState.mViewedParticipantIndex) { return; } pCars2APIParticipantStruct playerData = currentState.mParticipantData[playerIndex]; if (currentGameState != null && currentGameState.Now > nextCarClassCheckDue) { var carClass = currentGameState.carClass; if (carClass != null && !String.Equals(currentPlayerCarClassID, carClass.getClassIdentifier())) { // Retrieve and use user overridable spotter car length/width. this.internalSpotter.setCarDimensions(GlobalBehaviourSettings.spotterVehicleLength, GlobalBehaviourSettings.spotterVehicleWidth); this.currentPlayerCarClassID = carClass.getClassIdentifier(); } nextCarClassCheckDue = currentGameState.Now.AddSeconds(5); } float[] currentPlayerPosition = new float[] { playerData.mWorldPosition[0], playerData.mWorldPosition[2] }; List <float[]> currentOpponentPositions = new List <float[]>(); float[] playerVelocityData = new float[3]; playerVelocityData[0] = currentState.mSpeed; playerVelocityData[1] = currentState.mWorldVelocity[0]; playerVelocityData[2] = currentState.mWorldVelocity[2]; positionsFilledForThisTick.Clear(); positionsFilledForThisTick.Add(playerData.mRacePosition); for (int i = 0; i < currentState.mParticipantData.Count(); i++) { if (i == playerIndex) { continue; } pCars2APIParticipantStruct opponentData = currentState.mParticipantData[i]; if (opponentData.mIsActive && !positionsFilledForThisTick.Contains(opponentData.mRacePosition)) { float[] currentPositions = new float[] { opponentData.mWorldPosition[0], opponentData.mWorldPosition[2] }; currentOpponentPositions.Add(currentPositions); positionsFilledForThisTick.Add(opponentData.mRacePosition); } } if (currentOpponentPositions.Count() > 0) { float playerRotation = currentState.mOrientation[1]; if (playerRotation < 0) { playerRotation = playerRotation * -1; } else { playerRotation = twoPi - playerRotation; } internalSpotter.triggerInternal(playerRotation, currentPlayerPosition, playerVelocityData, currentOpponentPositions); } } }
public static pCars2APIStruct MergeWithExistingState(pCars2APIStruct existingState, sTelemetryData udpTelemetryData) { // Participant Info existingState.mViewedParticipantIndex = udpTelemetryData.sViewedParticipantIndex; // Unfiltered Input existingState.mUnfilteredThrottle = (float)udpTelemetryData.sUnfilteredThrottle / 255f; existingState.mUnfilteredBrake = (float)udpTelemetryData.sUnfilteredBrake / 255f; existingState.mUnfilteredSteering = (float)udpTelemetryData.sUnfilteredSteering / 127f; existingState.mUnfilteredClutch = (float)udpTelemetryData.sUnfilteredClutch / 255f; // Car State existingState.mCarFlags = udpTelemetryData.sCarFlags; existingState.mOilTempCelsius = udpTelemetryData.sOilTempCelsius; existingState.mOilPressureKPa = udpTelemetryData.sOilPressureKPa; existingState.mWaterTempCelsius = udpTelemetryData.sWaterTempCelsius; existingState.mWaterPressureKPa = udpTelemetryData.sWaterPressureKpa; existingState.mFuelPressureKPa = udpTelemetryData.sFuelPressureKpa; existingState.mFuelLevel = udpTelemetryData.sFuelLevel; existingState.mFuelCapacity = udpTelemetryData.sFuelCapacity; existingState.mSpeed = udpTelemetryData.sSpeed; existingState.mMaxRPM = udpTelemetryData.sMaxRpm; existingState.mBrake = (float)udpTelemetryData.sBrake / 255f; existingState.mThrottle = (float)udpTelemetryData.sThrottle / 255f; existingState.mClutch = (float)udpTelemetryData.sClutch / 255f; existingState.mSteering = (float)udpTelemetryData.sSteering / 127f; existingState.mGear = udpTelemetryData.sGearNumGears & 15; existingState.mNumGears = udpTelemetryData.sGearNumGears >> 4; existingState.mOdometerKM = udpTelemetryData.sOdometerKM; existingState.mBoostAmount = udpTelemetryData.sBoostAmount; // Motion & Device Related existingState.mOrientation = udpTelemetryData.sOrientation; existingState.mLocalVelocity = udpTelemetryData.sLocalVelocity; existingState.mWorldVelocity = udpTelemetryData.sWorldVelocity; existingState.mAngularVelocity = udpTelemetryData.sAngularVelocity; existingState.mLocalAcceleration = udpTelemetryData.sLocalAcceleration; existingState.mWorldAcceleration = udpTelemetryData.sWorldAcceleration; existingState.mExtentsCentre = udpTelemetryData.sExtentsCentre; existingState.mTyreFlags = toUIntArray(udpTelemetryData.sTyreFlags); existingState.mTerrain = toUIntArray(udpTelemetryData.sTerrain); existingState.mTyreY = udpTelemetryData.sTyreY; existingState.mTyreRPS = udpTelemetryData.sTyreRPS; existingState.mTyreTemp = toFloatArray(udpTelemetryData.sTyreTemp, 255); existingState.mTyreHeightAboveGround = udpTelemetryData.sTyreHeightAboveGround; existingState.mTyreWear = toFloatArray(udpTelemetryData.sTyreWear, 255); existingState.mBrakeTempCelsius = toFloatArray(udpTelemetryData.sBrakeTempCelsius, 1); existingState.mTyreTreadTemp = toFloatArray(udpTelemetryData.sTyreTreadTemp, 1); existingState.mTyreLayerTemp = toFloatArray(udpTelemetryData.sTyreLayerTemp, 1); existingState.mTyreCarcassTemp = toFloatArray(udpTelemetryData.sTyreCarcassTemp, 1); existingState.mTyreRimTemp = toFloatArray(udpTelemetryData.sTyreRimTemp, 1); existingState.mTyreInternalAirTemp = toFloatArray(udpTelemetryData.sTyreInternalAirTemp, 1); // IMO tyre temps aren't mapped here - they're in UDP but not MMF existingState.mWheelLocalPositionY = udpTelemetryData.sWheelLocalPositionY; existingState.mRideHeight = udpTelemetryData.sRideHeight; existingState.mSuspensionTravel = udpTelemetryData.sSuspensionTravel; existingState.mSuspensionRideHeight = toFloatArray(udpTelemetryData.sSuspensionRideHeight, 1); existingState.mSuspensionVelocity = udpTelemetryData.sSuspensionVelocity; existingState.mAirPressure = toFloatArray(udpTelemetryData.sAirPressure, 1); existingState.mEngineSpeed = udpTelemetryData.sEngineSpeed; existingState.mEngineTorque = udpTelemetryData.sEngineTorque; // Car Damage existingState.mCrashState = udpTelemetryData.sCrashState; existingState.mAeroDamage = (float)udpTelemetryData.sAeroDamage / 255f; existingState.mEngineDamage = (float)udpTelemetryData.sEngineDamage / 255f; existingState.mBrakeDamage = toFloatArray(udpTelemetryData.sBrakeDamage, 255); existingState.mSuspensionDamage = toFloatArray(udpTelemetryData.sSuspensionDamage, 255); existingState.mWings = toFloatArray(udpTelemetryData.sWings, 1); existingState.mJoyPad1 = udpTelemetryData.sJoyPad1; existingState.mJoyPad2 = udpTelemetryData.sJoyPad2; existingState.mDPad = udpTelemetryData.sDPad; // tyres existingState.mLFTyreCompoundName = udpTelemetryData.lfTyreCompound; existingState.mRFTyreCompoundName = udpTelemetryData.rfTyreCompound; existingState.mLRTyreCompoundName = udpTelemetryData.lrTyreCompound; existingState.mRRTyreCompoundName = udpTelemetryData.rrTyreCompound; return(existingState); }
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); }