diff --git a/CHANGELOG.md b/CHANGELOG.md index 04146612..f945ff98 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,7 @@ - IRacingPlugin: Rename IRacingCompletedLap.Time to IRacingCompletedLap.LapTime. - IRacingPlugin: Add IRacingCompletedLap.EstimatedLapTime - to show if this was a laptime as recorded by IRacing or by Slipstream in case IRacing doesn't provide a laptime - IRacingPlugin: Change IRacingDriverIncident: Rename Incident(Count|Delta) to DriverIncident(Count|Delta). Added fields: TeamIncident(Count|Delta) and MyIncident(Count|Delta) + - IRacingPlugin: Adds: IRacingTowed event ## [0.4.0](https://github.com/dennis/slipstream/releases/tag/v0.3.0) (2020-01-10) [Full Changelog](https://github.com/dennis/slipstream/compare/v0.3.0...v0.4.0) diff --git a/Components/IRacing/EventFactory/IRacingEventFactory.cs b/Components/IRacing/EventFactory/IRacingEventFactory.cs index 6cb8f3ca..71148d0f 100644 --- a/Components/IRacing/EventFactory/IRacingEventFactory.cs +++ b/Components/IRacing/EventFactory/IRacingEventFactory.cs @@ -1,5 +1,6 @@ using Slipstream.Components.IRacing.Events; using Slipstream.Components.IRacing.Plugins.GameState; +using Slipstream.Shared; using System; using static Slipstream.Components.IRacing.IIRacingEventFactory; @@ -431,5 +432,14 @@ public IRacingRaw CreateIRacingRaw(IState state) CurrentState = state }; } + + public IEvent CreateIRacingTowed(double sessionTime, float remainingTowTime) + { + return new IRacingTowed + { + SessionTime = sessionTime, + RemainingTowTime = remainingTowTime, + }; + } } } \ No newline at end of file diff --git a/Components/IRacing/EventHandler/IRacing.cs b/Components/IRacing/EventHandler/IRacing.cs index 26bb5b74..f8e4c20d 100644 --- a/Components/IRacing/EventHandler/IRacing.cs +++ b/Components/IRacing/EventHandler/IRacing.cs @@ -60,6 +60,8 @@ public IRacing(EventHandlerController eventHandler) public delegate void OnIRacingRawHandler(EventHandlerController source, EventHandlerArgs e); + public delegate void OnIRacingTowedHandler(EventHandlerController source, EventHandlerArgs e); + public event OnIRacingCarCompletedLapHandler? OnIRacingCarCompletedLap; public event OnIRacingCarInfoHandler? OnIRacingCarInfo; @@ -106,6 +108,8 @@ public IRacing(EventHandlerController eventHandler) public event OnIRacingRawHandler? OnIRacingRaw; + public event OnIRacingTowedHandler? OnIRacingTowed; + public IEventHandler.HandledStatus HandleEvent(IEvent @event) { switch (@event) @@ -340,6 +344,16 @@ public IEventHandler.HandledStatus HandleEvent(IEvent @event) { return IEventHandler.HandledStatus.UseDefault; } + case IRacingTowed tev: + if (OnIRacingTowed != null) + { + OnIRacingTowed(Parent, new EventHandlerArgs(tev)); + return IEventHandler.HandledStatus.Handled; + } + else + { + return IEventHandler.HandledStatus.UseDefault; + } } return IEventHandler.HandledStatus.NotMine; diff --git a/Components/IRacing/Events/IRacingTowed.cs b/Components/IRacing/Events/IRacingTowed.cs new file mode 100644 index 00000000..324708a2 --- /dev/null +++ b/Components/IRacing/Events/IRacingTowed.cs @@ -0,0 +1,33 @@ +using Slipstream.Shared; +using System.Collections.Generic; + +namespace Slipstream.Components.IRacing.Events +{ + public class IRacingTowed : IEvent + { + public string EventType => "IRacingTowed"; + public bool ExcludeFromTxrx => false; + public ulong Uptime { get; set; } + public double SessionTime { get; set; } + public double RemainingTowTime { get; set; } + + public override bool Equals(object obj) + { + return obj is IRacingTowed other && + EventType == other.EventType && + ExcludeFromTxrx == other.ExcludeFromTxrx && + SessionTime == other.SessionTime && + RemainingTowTime == other.RemainingTowTime; + } + + public override int GetHashCode() + { + int hashCode = 2084919435; + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(EventType); + hashCode = hashCode * -1521134295 + ExcludeFromTxrx.GetHashCode(); + hashCode = hashCode * -1521134295 + SessionTime.GetHashCode(); + hashCode = hashCode * -1521134295 + RemainingTowTime.GetHashCode(); + return hashCode; + } + } +} \ No newline at end of file diff --git a/Components/IRacing/IIRacingEventFactory.cs b/Components/IRacing/IIRacingEventFactory.cs index 21160669..a761be1b 100644 --- a/Components/IRacing/IIRacingEventFactory.cs +++ b/Components/IRacing/IIRacingEventFactory.cs @@ -1,5 +1,6 @@ using Slipstream.Components.IRacing.Events; using Slipstream.Components.IRacing.Plugins.GameState; +using Slipstream.Shared; #nullable enable @@ -47,6 +48,8 @@ IRacingCompletedLap CreateIRacingCompletedLap( bool bestLap ); + IEvent CreateIRacingTowed(double sessionTime, float remainingTowTime); + IRacingCarInfo CreateIRacingCarInfo( double sessionTime, long carIdx, diff --git a/Components/IRacing/Plugins/GameState/IState.cs b/Components/IRacing/Plugins/GameState/IState.cs index 4965f6c5..aec2af6e 100644 --- a/Components/IRacing/Plugins/GameState/IState.cs +++ b/Components/IRacing/Plugins/GameState/IState.cs @@ -58,5 +58,6 @@ public interface IState public IRacingSessionTypeEnum SessionType { get; } public ISession[] Sessions { get; } public ISession CurrentSession { get; } + public float PlayerCarTowTime { get; } } } \ No newline at end of file diff --git a/Components/IRacing/Plugins/GameState/State.cs b/Components/IRacing/Plugins/GameState/State.cs index 3d959d76..b7acbbbe 100644 --- a/Components/IRacing/Plugins/GameState/State.cs +++ b/Components/IRacing/Plugins/GameState/State.cs @@ -58,8 +58,9 @@ public class State : IState public IRacingSessionTypeEnum SessionType { get; set; } = IRacingSessionTypeEnum.Practice; public ISession[] Sessions { get; set; } = new Session[] { }; public ISession CurrentSession { get => Sessions[SessionNum]; } - public int MyIncidentCount { get; internal set; } - public int TeamIncidentCount { get; internal set; } + public int MyIncidentCount { get; set; } + public int TeamIncidentCount { get; set; } + public float PlayerCarTowTime { get; set; } public State() { @@ -134,6 +135,7 @@ public IState Clone() SessionState = SessionState, SessionType = SessionType, Sessions = sessions.ToArray(), + PlayerCarTowTime = PlayerCarTowTime, }; } } diff --git a/Components/IRacing/Plugins/GameState/StateFactory.cs b/Components/IRacing/Plugins/GameState/StateFactory.cs index d97c9966..ae3753e2 100644 --- a/Components/IRacing/Plugins/GameState/StateFactory.cs +++ b/Components/IRacing/Plugins/GameState/StateFactory.cs @@ -112,6 +112,8 @@ internal class StateFactory : IStateFactory FogLevel = ds.Telemetry.FogLevel, RaceCategory = IRacingCategoryTypes[ds.SessionData.WeekendInfo.Category], + + PlayerCarTowTime = (float)ds.Telemetry["PlayerCarTowTime"], }; { diff --git a/Components/IRacing/Plugins/Trackers/TowTracker.cs b/Components/IRacing/Plugins/Trackers/TowTracker.cs new file mode 100644 index 00000000..738aefc3 --- /dev/null +++ b/Components/IRacing/Plugins/Trackers/TowTracker.cs @@ -0,0 +1,44 @@ +using Slipstream.Components.IRacing.Plugins.Models; +using Slipstream.Shared; +using System.Diagnostics; + +#nullable enable + +namespace Slipstream.Components.IRacing.Plugins.Trackers +{ + internal class TowTracker : IIRacingDataTracker + { + private readonly IIRacingEventFactory EventFactory; + private readonly IEventBus EventBus; + private bool BeingTowed = false; + + public TowTracker(IEventBus eventBus, IIRacingEventFactory eventFactory) + { + EventBus = eventBus; + EventFactory = eventFactory; + } + + public void Handle(GameState.IState currentState, IRacingDataTrackerState state) + { + var towTime = currentState.PlayerCarTowTime; + + if (towTime > 0) + { + if (!BeingTowed) + { + Debug.WriteLine($"TOW: {towTime}"); + BeingTowed = true; + + EventBus.PublishEvent(EventFactory.CreateIRacingTowed( + sessionTime: currentState.SessionTime, + remainingTowTime: towTime + )); + } + } + else + { + BeingTowed = false; + } + } + } +} \ No newline at end of file diff --git a/Components/IRacing/Plugins/Trackers/Trackers.cs b/Components/IRacing/Plugins/Trackers/Trackers.cs index ea435750..7c040e8c 100644 --- a/Components/IRacing/Plugins/Trackers/Trackers.cs +++ b/Components/IRacing/Plugins/Trackers/Trackers.cs @@ -28,6 +28,7 @@ public Trackers(IEventBus eventBus, IIRacingEventFactory eventFactory) DataTrackers.Add(new PitUsageTracker(eventBus, eventFactory)); DataTrackers.Add(new IRacingSessionTracker(eventBus, eventFactory)); DataTrackers.Add(new CarPositionTracker(eventBus, eventFactory)); + DataTrackers.Add(new TowTracker(eventBus, eventFactory)); } public void Handle(GameState.IState currentState) diff --git a/Components/IRacing/README.md b/Components/IRacing/README.md index b3aee43b..f0bb03c4 100644 --- a/Components/IRacing/README.md +++ b/Components/IRacing/README.md @@ -538,7 +538,7 @@ Published every time car changes positions. | EventType | string | `IRacingCarPosition` (constant) | | ExcludeFromTxrx | boolean | false (constant) | | Uptime | integer | Time of when the message was sent via Eventbus (in milliseconds). | -| SessionTime | string | | +| SessionTime | float | | | CarIdx | int | Car Index | | LocalUser | bool | Is it our car? | | PositionInClass | int | Position in class | @@ -547,3 +547,19 @@ Published every time car changes positions. **JSON Example:** `{"EventType":"IRacingCarPosition","ExcludeFromTxrx":false,"Uptime":2528216,"SessionTime":2711.7666666666669,"CarIdx":28,"LocalUser":true,"PositionInClass":15,"PositionInRace":15}` + +
IRacingTowed
+ +Published every your car is being towed to pits. You will not get these events for other cars. These isn't published during replays. + +| Name | Type | Description | +|:-----------------|:-------:|:------------------------------------------------------------------| +| EventType | string | `IRacingTowed` (constant) | +| ExcludeFromTxrx | boolean | false (constant) | +| Uptime | integer | Time of when the message was sent via Eventbus (in milliseconds). | +| SessionTime | float | | +| RemainingTowTime | float | How many seconds before we're in pit | + +**JSON Example:** +`{"EventType":"IRacingTowed","ExcludeFromTxrx":false,"Uptime":2528216,"SessionTime":2711.7666666666669,"CarIdx":28,"LocalUser":true,"PositionInClass":15,"PositionInRace":15}` +
diff --git a/Slipstream.UnitTests/Components/IRacing/Plugins/Trackers/TowTrackerTests.cs b/Slipstream.UnitTests/Components/IRacing/Plugins/Trackers/TowTrackerTests.cs new file mode 100644 index 00000000..878dc274 --- /dev/null +++ b/Slipstream.UnitTests/Components/IRacing/Plugins/Trackers/TowTrackerTests.cs @@ -0,0 +1,82 @@ +using Slipstream.Components.IRacing; +using Slipstream.Components.IRacing.EventFactory; +using Slipstream.Components.IRacing.Events; +using Slipstream.Components.IRacing.Plugins.GameState; +using Slipstream.Components.IRacing.Plugins.Models; +using Slipstream.Components.IRacing.Plugins.Trackers; +using Slipstream.UnitTests.TestData; +using Xunit; + +namespace Slipstream.UnitTests.Components.IRacing.Plugins.Trackers +{ + public class TowTrackerTests + { + private readonly TestEventBus EventBus; + private readonly IIRacingEventFactory EventFactory; + private readonly IRacingDataTrackerState TrackerState; + private readonly GameStateBuilder Builder; + + public TowTrackerTests() + { + EventBus = new TestEventBus(); + EventFactory = new IRacingEventFactory(); + TrackerState = new IRacingDataTrackerState(); + Builder = new GameStateBuilder(); + } + + [Fact] + public void PublishEventIfBeginTowed() + { + const float NOW = 1234.2f; + const float TOW_TIME = 32.2f; + + // arrange + Builder.AtSessionTime(NOW).Set(a => a.PlayerCarTowTime = TOW_TIME).Commit(); + Builder.AtSessionTime(NOW + 1).Set(a => a.PlayerCarTowTime = TOW_TIME - 1).Commit(); + Builder.AtSessionTime(NOW + 2).Set(a => a.PlayerCarTowTime = TOW_TIME - 2).Commit(); + + // act + var sut = new TowTracker(EventBus, EventFactory); + foreach (var s in Builder.States) + sut.Handle(s, TrackerState); + + // assert + Assert.True(EventBus.Events.Count == 1); + + var @event = EventBus.Events[0] as IRacingTowed; + Assert.NotNull(@event); + Assert.True(@event.SessionTime == NOW); + Assert.True(@event.RemainingTowTime == TOW_TIME); + } + + [Fact] + public void PublishEventsIfPlayerGotAReallyShittyRace() + { + const float NOW = 1234.2f; + const float TOW_TIME = 32.2f; + const float TOW2_TIME = 10f; + + // arrange + // first two + Builder.AtSessionTime(NOW).Set(a => a.PlayerCarTowTime = TOW_TIME).Commit(); + Builder.AtSessionTime(NOW + 1).Set(a => a.PlayerCarTowTime = TOW_TIME - 1).Commit(); + // all ok again + Builder.AtSessionTime(NOW + 2).Set(a => a.PlayerCarTowTime = 0).Commit(); + // another tow + Builder.AtSessionTime(NOW + 3).Set(a => a.PlayerCarTowTime = TOW2_TIME).Commit(); + + // act + var sut = new TowTracker(EventBus, EventFactory); + foreach (var s in Builder.States) + sut.Handle(s, TrackerState); + + // assert + Assert.True(EventBus.Events.Count == 2); + + var @event = EventBus.Events[1] as IRacingTowed; + Assert.NotNull(@event); + Assert.True(@event.SessionTime == NOW + 3); + Assert.True(@event.RemainingTowTime == TOW2_TIME); + } + } +} \ No newline at end of file diff --git a/Slipstream.UnitTests/TestData/TestState.cs b/Slipstream.UnitTests/TestData/TestState.cs index b2d37555..39b3c934 100644 --- a/Slipstream.UnitTests/TestData/TestState.cs +++ b/Slipstream.UnitTests/TestData/TestState.cs @@ -106,11 +106,10 @@ public class TestState : IState public ISession[] Sessions { get; set; } = new Session[] { }; public ISession CurrentSession { get => Sessions[0]; } - public float LastLapTime { get; set; } - public int DriverIncidentCount { get; set; } public int MyIncidentCount { get; set; } + public float PlayerCarTowTime { get; set; } public TestState() { diff --git a/Slipstream.csproj b/Slipstream.csproj index 2bab9778..e8cccc6d 100644 --- a/Slipstream.csproj +++ b/Slipstream.csproj @@ -161,6 +161,7 @@ + @@ -177,6 +178,7 @@ +