public override Object ReadGameDataFromFile(String filename) { if (dataReadFromFile == null || filename != this.lastReadFileName) { this.dataReadFromFileIndex = 0; dataReadFromFile = DeSerializeObject <RF2StructWrapper[]>(dataFilesPath + filename); this.lastReadFileName = filename; } if (dataReadFromFile != null && dataReadFromFile.Length > this.dataReadFromFileIndex) { RF2StructWrapper structWrapperData = dataReadFromFile[this.dataReadFromFileIndex]; this.dataReadFromFileIndex++; return(structWrapperData); } else { return(null); } }
public override Object ReadGameDataFromFile(String filename, int pauseBeforeStart) { if (this.dataReadFromFile == null || filename != this.lastReadFileName) { this.dataReadFromFileIndex = 0; var filePathResolved = Utilities.ResolveDataFile(this.dataFilesPath, filename); dataReadFromFile = DeSerializeObject <RF2StructWrapper[]>(filePathResolved); this.lastReadFileName = filename; Thread.Sleep(pauseBeforeStart); } if (dataReadFromFile != null && dataReadFromFile.Length > this.dataReadFromFileIndex) { RF2StructWrapper structWrapperData = dataReadFromFile[this.dataReadFromFileIndex]; this.dataReadFromFileIndex++; return(structWrapperData); } else { return(null); } }
public override Object ReadGameData(Boolean forSpotter) { lock (this) { if (!this.initialised) { if (!this.InitialiseInternal()) { throw new GameDataReadException("Failed to initialise shared memory"); } } try { #if TRACE_BUFFER_READ_ELAPSED_TIME var watch = System.Diagnostics.Stopwatch.StartNew(); #endif extendedBuffer.GetMappedData(ref this.extended); telemetryBuffer.GetMappedData(ref this.telemetry); rulesBuffer.GetMappedData(ref this.rules); // Scoring is the most important game data in Crew Chief sense, // so acquire it last, hoping it will be most recent view of all buffer types. scoringBuffer.GetMappedData(ref this.scoring); // Create a new copy marshalled views. Thia is necessary because core code caches states, so each // state has to be an individual object. We can't avoid copy by marshalling directly into wrapper, // because not all marshalling calls fetch new buffer. var wrapper = new RF2StructWrapper() { extended = this.extended, telemetry = this.telemetry, rules = this.rules, // TODO_RF2: we probably don't need rules buffer if reading for spotter. scoring = this.scoring, ticksWhenRead = DateTime.UtcNow.Ticks }; if (!forSpotter && dumpToFile && this.dataToDump != null) { // Note: this is lossy save, because we only save update if Telemtry or Scoring changed. // Other buffers don't change that much, so it should be fine. // Exclude empty frames. if (wrapper.scoring.mScoringInfo.mNumVehicles > 0 && wrapper.extended.mSessionStarted == 1) { var hasTelemetryChanged = false; if (wrapper.telemetry.mNumVehicles > 0) { var currTelET = wrapper.telemetry.mVehicles[0].mElapsedTime; hasTelemetryChanged = currTelET != this.lastTelemetryET; this.lastTelemetryET = currTelET; } var currScoringET = wrapper.scoring.mScoringInfo.mCurrentET; if (currScoringET != this.lastScoringET || // scoring contains new payload hasTelemetryChanged) // Or, telemetry updated. { // NOTE: truncation code could be moved to DumpRawGameData method for reduced CPU use. // However, this causes memory pressure (~250Mb/minute with 22 vehicles), so probably better done here. wrapper.telemetry.mVehicles = this.GetPopulatedVehicleInfoArray <rF2VehicleTelemetry>(wrapper.telemetry.mVehicles, wrapper.telemetry.mNumVehicles); wrapper.scoring.mVehicles = this.GetPopulatedVehicleInfoArray <rF2VehicleScoring>(wrapper.scoring.mVehicles, wrapper.scoring.mScoringInfo.mNumVehicles); // For rules, exclude empty messages from serialization. wrapper.rules.mTrackRules.mMessage = wrapper.rules.mTrackRules.mMessage[0] != 0 ? wrapper.rules.mTrackRules.mMessage : null; wrapper.rules.mParticipants = this.GetPopulatedVehicleInfoArray <rF2TrackRulesParticipant>(wrapper.rules.mParticipants, wrapper.rules.mTrackRules.mNumParticipants); for (int i = 0; i < wrapper.rules.mParticipants.Length; ++i) { wrapper.rules.mParticipants[i].mMessage = wrapper.rules.mParticipants[i].mMessage[0] != 0 ? wrapper.rules.mParticipants[i].mMessage : null; } int maxmID = 0; foreach (var vehicleScoring in wrapper.scoring.mVehicles) { maxmID = Math.Max(maxmID, vehicleScoring.mID); } if (maxmID < rFactor2Constants.MAX_MAPPED_IDS) { // Since serialization to XML produces a lot of useless tags even for small arrays, truncate tracked damage array. // It is indexed by mID. Max mID in current set is equal to mNumVehicles in 99% of cases, so just truncate to this size. wrapper.extended.mTrackedDamages = this.GetPopulatedVehicleInfoArray <rF2TrackedDamage>(wrapper.extended.mTrackedDamages, maxmID + 1); } this.dataToDump.Add(wrapper); this.lastScoringET = currScoringET; } } } #if TRACE_BUFFER_READ_ELAPSED_TIME watch.Stop(); var microseconds = watch.ElapsedTicks * 1000000 / System.Diagnostics.Stopwatch.Frequency; System.Console.WriteLine("Buffer read microseconds: " + microseconds); #endif return(wrapper); } catch (Exception ex) { Console.WriteLine("rFactor 2 Shared Memory connection failed."); this.DisconnectInternal(); throw new GameDataReadException(ex.Message, ex); } } }
public override Object ReadGameData(Boolean forSpotter) { lock (this) { var rF2StateMarshalled = new rF2State(); if (!initialised) { if (!this.InitialiseInternal()) { throw new GameDataReadException("Failed to initialise shared memory"); } } try { if (this.fileAccessMutex.WaitOne(5000)) { try { bool buf1Current = false; // Try buffer 1: using (var sharedMemoryStreamView = this.memoryMappedFile1.CreateViewStream()) { var sharedMemoryStream = new BinaryReader(sharedMemoryStreamView); this.sharedMemoryReadBuffer = sharedMemoryStream.ReadBytes(this.SHARED_MEMORY_HEADER_SIZE_BYTES); // Marhsal header var headerHandle = GCHandle.Alloc(this.sharedMemoryReadBuffer, GCHandleType.Pinned); var header = (rF2StateHeader)Marshal.PtrToStructure(headerHandle.AddrOfPinnedObject(), typeof(rF2StateHeader)); headerHandle.Free(); if (header.mCurrentRead == 1) { sharedMemoryStream.BaseStream.Position = 0; this.sharedMemoryReadBuffer = sharedMemoryStream.ReadBytes(this.SHARED_MEMORY_SIZE_BYTES); buf1Current = true; } } // Read buffer 2 if (!buf1Current) { using (var sharedMemoryStreamView = this.memoryMappedFile2.CreateViewStream()) { var sharedMemoryStream = new BinaryReader(sharedMemoryStreamView); this.sharedMemoryReadBuffer = sharedMemoryStream.ReadBytes(this.SHARED_MEMORY_SIZE_BYTES); } } } finally { this.fileAccessMutex.ReleaseMutex(); } // Marshal rF2State var handle = GCHandle.Alloc(this.sharedMemoryReadBuffer, GCHandleType.Pinned); rF2StateMarshalled = (rF2State)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(rF2State)); handle.Free(); RF2StructWrapper structWrapper = new RF2StructWrapper(); structWrapper.ticksWhenRead = DateTime.Now.Ticks; structWrapper.state = rF2StateMarshalled; if (!forSpotter && dumpToFile && this.dataToDump != null) { this.dataToDump.Add(structWrapper); } return(structWrapper); } else { Console.WriteLine("Timed out waiting on rFactor 2 Shared Memory mutex."); return(null); } } catch (Exception ex) { Console.WriteLine("rFactor 2 Shared Memory connection failed."); this.Disconnect(); throw new GameDataReadException(ex.Message, ex); } } }