diff --git a/.editorconfig b/.editorconfig index ed2d0ac..f8f556e 100644 --- a/.editorconfig +++ b/.editorconfig @@ -228,10 +228,12 @@ dotnet_naming_style.begins_with_i.required_suffix = dotnet_naming_style.begins_with_i.word_separator = dotnet_naming_style.begins_with_i.capitalization = pascal_case csharp_style_prefer_primary_constructors = true:suggestion +csharp_prefer_system_threading_lock = true:suggestion [*.{cs,vb}] dotnet_style_operator_placement_when_wrapping = beginning_of_line tab_width = 4 +indent_style = tab indent_size = 4 end_of_line = crlf dotnet_style_coalesce_expression = true:warning diff --git a/F1Game.UDP.Benchamrks/F1Game.UDP.Benchmarks.csproj b/F1Game.UDP.Benchamrks/F1Game.UDP.Benchmarks.csproj index 842dcdf..0d53da2 100644 --- a/F1Game.UDP.Benchamrks/F1Game.UDP.Benchmarks.csproj +++ b/F1Game.UDP.Benchamrks/F1Game.UDP.Benchmarks.csproj @@ -2,10 +2,11 @@ Exe - net8.0 + net8.0;net9.0 enable enable false + latest diff --git a/F1Game.UDP.Benchamrks/Program.cs b/F1Game.UDP.Benchamrks/Program.cs index 2266826..1233a2a 100644 --- a/F1Game.UDP.Benchamrks/Program.cs +++ b/F1Game.UDP.Benchamrks/Program.cs @@ -4,7 +4,8 @@ using BenchmarkDotNet.Running; var config = DefaultConfig.Instance - .AddJob(Job.Default.WithId("NET8").WithRuntime(CoreRuntime.Core80)); + .AddJob(Job.Default.WithId("NET8").WithRuntime(CoreRuntime.Core80)) + .AddJob(Job.Default.WithId("NET9").WithRuntime(CoreRuntime.Core90)); BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly) .Run(args, config); diff --git a/F1Game.UDP.Benchamrks/ThirdPartyComparisonBenchmark.cs b/F1Game.UDP.Benchamrks/ThirdPartyComparisonBenchmark.cs index 9da6f94..e90a71c 100644 --- a/F1Game.UDP.Benchamrks/ThirdPartyComparisonBenchmark.cs +++ b/F1Game.UDP.Benchamrks/ThirdPartyComparisonBenchmark.cs @@ -77,7 +77,9 @@ public Packet ReadF1Simracing() static void SetupCarTelemetryPacket(byte[] data, Random random) { - var packet = data.ToPacket().CarTelemetryDataPacket; + if (!data.ToPacket().TryGetCarTelemetryDataPacket(out var packet)) + return; + var updatedPacket = packet with { CarTelemetryData = packet.CarTelemetryData.AsEnumerable().Select(x => x with @@ -93,7 +95,9 @@ static void SetupCarTelemetryPacket(byte[] data, Random random) static void SetupCarStatusPacket(byte[] data, Random random) { - var packet = data.ToPacket().CarStatusDataPacket; + if (!data.ToPacket().TryGetCarStatusDataPacket(out var packet)) + return; + var updatedPacket = packet with { CarStatusData = packet.CarStatusData.AsEnumerable().Select(x => x with @@ -113,7 +117,9 @@ static void SetupEventPacket(byte[] data) static void SetupSessionPacket(byte[] data, Random random) { - var packet = data.ToPacket().SessionDataPacket; + if (!data.ToPacket().TryGetSessionDataPacket(out var packet)) + return; + var updatedPacket = packet with { AirTemperature = (sbyte)random.Next(127), diff --git a/F1Game.UDP.Tests/F1Game.UDP.Tests.csproj b/F1Game.UDP.Tests/F1Game.UDP.Tests.csproj index cbacf07..4ed0551 100644 --- a/F1Game.UDP.Tests/F1Game.UDP.Tests.csproj +++ b/F1Game.UDP.Tests/F1Game.UDP.Tests.csproj @@ -1,7 +1,7 @@  - net8.0 + net8.0;net9.0 enable enable false diff --git a/F1Game.UDP.Tests/PacketReaderFixture.cs b/F1Game.UDP.Tests/PacketReaderFixture.cs index 66abb85..38f12ae 100644 --- a/F1Game.UDP.Tests/PacketReaderFixture.cs +++ b/F1Game.UDP.Tests/PacketReaderFixture.cs @@ -7,6 +7,8 @@ using F1Game.UDP.Internal; using F1Game.UDP.Packets; +using AwesomeAssertions.Equivalency; + namespace F1Game.UDP.Tests; [FixtureLifeCycle(LifeCycle.InstancePerTestCase)] @@ -75,9 +77,9 @@ public void ReadCarDamageDataPacket() var writer = new BytesWriter(bytes); writer.Write(packet); - bytes.ToPacket().Should().BeEquivalentTo(packet); - bytes.ToPacketWithReader().Should().BeEquivalentTo(packet); - bytes.ToPacketWithMarshal().Should().BeEquivalentTo(packet); + bytes.ToPacket().Should().BeEquivalentTo(packet, Configure); + bytes.ToPacketWithReader().Should().BeEquivalentTo(packet, Configure); + bytes.ToPacketWithMarshal().Should().BeEquivalentTo(packet, Configure); } [Test] @@ -91,9 +93,9 @@ public void ReadCarSetupDataPacket() var writer = new BytesWriter(bytes); writer.Write(packet); - bytes.ToPacket().Should().BeEquivalentTo(packet); - bytes.ToPacketWithReader().Should().BeEquivalentTo(packet); - bytes.ToPacketWithMarshal().Should().BeEquivalentTo(packet); + bytes.ToPacket().Should().BeEquivalentTo(packet, Configure); + bytes.ToPacketWithReader().Should().BeEquivalentTo(packet, Configure); + bytes.ToPacketWithMarshal().Should().BeEquivalentTo(packet, Configure); } [Test] @@ -107,9 +109,9 @@ public void ReadCarStatusDataPacket() var writer = new BytesWriter(bytes); writer.Write(packet); - bytes.ToPacket().Should().BeEquivalentTo(packet); - bytes.ToPacketWithReader().Should().BeEquivalentTo(packet); - bytes.ToPacketWithMarshal().Should().BeEquivalentTo(packet); + bytes.ToPacket().Should().BeEquivalentTo(packet, Configure); + bytes.ToPacketWithReader().Should().BeEquivalentTo(packet, Configure); + bytes.ToPacketWithMarshal().Should().BeEquivalentTo(packet, Configure); } [Test] @@ -123,9 +125,9 @@ public void ReadCarTelemetryDataPacket() var writer = new BytesWriter(bytes); writer.Write(packet); - bytes.ToPacket().Should().BeEquivalentTo(packet); - bytes.ToPacketWithReader().Should().BeEquivalentTo(packet); - bytes.ToPacketWithMarshal().Should().BeEquivalentTo(packet); + bytes.ToPacket().Should().BeEquivalentTo(packet, Configure); + bytes.ToPacketWithReader().Should().BeEquivalentTo(packet, Configure); + bytes.ToPacketWithMarshal().Should().BeEquivalentTo(packet, Configure); } [TestCase(EventType.ButtonStatus)] @@ -178,9 +180,9 @@ public void ReadEventDataPacket(EventType eventType) var writer = new BytesWriter(bytes); writer.Write(packet); - bytes.ToPacket().Should().BeEquivalentTo(packet); - bytes.ToPacketWithReader().Should().BeEquivalentTo(packet); - bytes.ToPacketWithMarshal().Should().BeEquivalentTo(packet); + bytes.ToPacket().Should().BeEquivalentTo(packet, Configure); + bytes.ToPacketWithReader().Should().BeEquivalentTo(packet, Configure); + bytes.ToPacketWithMarshal().Should().BeEquivalentTo(packet, Configure); } [Test] @@ -201,9 +203,9 @@ public void ReadFinalClassificationDataPacket() var writer = new BytesWriter(bytes); writer.Write(packet); - bytes.ToPacket().Should().BeEquivalentTo(packet); - bytes.ToPacketWithReader().Should().BeEquivalentTo(packet); - bytes.ToPacketWithMarshal().Should().BeEquivalentTo(packet); + bytes.ToPacket().Should().BeEquivalentTo(packet, Configure); + bytes.ToPacketWithReader().Should().BeEquivalentTo(packet, Configure); + bytes.ToPacketWithMarshal().Should().BeEquivalentTo(packet, Configure); } [Test] @@ -217,9 +219,9 @@ public void ReadLapDataPacket() var writer = new BytesWriter(bytes); writer.Write(packet); - bytes.ToPacket().Should().BeEquivalentTo(packet); - bytes.ToPacketWithReader().Should().BeEquivalentTo(packet); - bytes.ToPacketWithMarshal().Should().BeEquivalentTo(packet); + bytes.ToPacket().Should().BeEquivalentTo(packet, Configure); + bytes.ToPacketWithReader().Should().BeEquivalentTo(packet, Configure); + bytes.ToPacketWithMarshal().Should().BeEquivalentTo(packet, Configure); } [Test] @@ -238,9 +240,9 @@ public void ReadLobbyInfoDataPacket() var writer = new BytesWriter(bytes); writer.Write(packet); - bytes.ToPacket().Should().BeEquivalentTo(packet); - bytes.ToPacketWithReader().Should().BeEquivalentTo(packet); - bytes.ToPacketWithMarshal().Should().BeEquivalentTo(packet); + bytes.ToPacket().Should().BeEquivalentTo(packet, Configure); + bytes.ToPacketWithReader().Should().BeEquivalentTo(packet, Configure); + bytes.ToPacketWithMarshal().Should().BeEquivalentTo(packet, Configure); } [Test] @@ -254,9 +256,9 @@ public void ReadMotionDataPacket() var writer = new BytesWriter(bytes); writer.Write(packet); - bytes.ToPacket().Should().BeEquivalentTo(packet); - bytes.ToPacketWithReader().Should().BeEquivalentTo(packet); - bytes.ToPacketWithMarshal().Should().BeEquivalentTo(packet); + bytes.ToPacket().Should().BeEquivalentTo(packet, Configure); + bytes.ToPacketWithReader().Should().BeEquivalentTo(packet, Configure); + bytes.ToPacketWithMarshal().Should().BeEquivalentTo(packet, Configure); } [Test] @@ -269,9 +271,9 @@ public void ReadMotionExDataPacket() var writer = new BytesWriter(bytes); writer.Write(packet); - bytes.ToPacket().Should().BeEquivalentTo(packet); - bytes.ToPacketWithReader().Should().BeEquivalentTo(packet); - bytes.ToPacketWithMarshal().Should().BeEquivalentTo(packet); + bytes.ToPacket().Should().BeEquivalentTo(packet, Configure); + bytes.ToPacketWithReader().Should().BeEquivalentTo(packet, Configure); + bytes.ToPacketWithMarshal().Should().BeEquivalentTo(packet, Configure); } [Test] @@ -290,9 +292,9 @@ public void ReadParticipantsDataPacket() var writer = new BytesWriter(bytes); writer.Write(packet); - bytes.ToPacket().Should().BeEquivalentTo(packet); - bytes.ToPacketWithReader().Should().BeEquivalentTo(packet); - bytes.ToPacketWithMarshal().Should().BeEquivalentTo(packet); + bytes.ToPacket().Should().BeEquivalentTo(packet, Configure); + bytes.ToPacketWithReader().Should().BeEquivalentTo(packet, Configure); + bytes.ToPacketWithMarshal().Should().BeEquivalentTo(packet, Configure); } [Test] @@ -308,9 +310,9 @@ public void ReadSessionDataPacket() var writer = new BytesWriter(bytes); writer.Write(packet); - bytes.ToPacket().Should().BeEquivalentTo(packet); - bytes.ToPacketWithReader().Should().BeEquivalentTo(packet); - bytes.ToPacketWithMarshal().Should().BeEquivalentTo(packet); + bytes.ToPacket().Should().BeEquivalentTo(packet, Configure); + bytes.ToPacketWithReader().Should().BeEquivalentTo(packet, Configure); + bytes.ToPacketWithMarshal().Should().BeEquivalentTo(packet, Configure); } [Test] @@ -325,9 +327,9 @@ public void ReadSessionHistoryDataPacket() var writer = new BytesWriter(bytes); writer.Write(packet); - bytes.ToPacket().Should().BeEquivalentTo(packet); - bytes.ToPacketWithReader().Should().BeEquivalentTo(packet); - bytes.ToPacketWithMarshal().Should().BeEquivalentTo(packet); + bytes.ToPacket().Should().BeEquivalentTo(packet, Configure); + bytes.ToPacketWithReader().Should().BeEquivalentTo(packet, Configure); + bytes.ToPacketWithMarshal().Should().BeEquivalentTo(packet, Configure); } [Test] @@ -341,9 +343,9 @@ public void ReadTyreSetsDataPacket() var writer = new BytesWriter(bytes); writer.Write(packet); - bytes.ToPacket().Should().BeEquivalentTo(packet); - bytes.ToPacketWithReader().Should().BeEquivalentTo(packet); - bytes.ToPacketWithMarshal().Should().BeEquivalentTo(packet); + bytes.ToPacket().Should().BeEquivalentTo(packet, Configure); + bytes.ToPacketWithReader().Should().BeEquivalentTo(packet, Configure); + bytes.ToPacketWithMarshal().Should().BeEquivalentTo(packet, Configure); } [Test] @@ -355,9 +357,9 @@ public void ReadTimeTrialDataPacket() var writer = new BytesWriter(bytes); writer.Write(packet); - bytes.ToPacket().Should().BeEquivalentTo(packet); - bytes.ToPacketWithReader().Should().BeEquivalentTo(packet); - bytes.ToPacketWithMarshal().Should().BeEquivalentTo(packet); + bytes.ToPacket().Should().BeEquivalentTo(packet, Configure); + bytes.ToPacketWithReader().Should().BeEquivalentTo(packet, Configure); + bytes.ToPacketWithMarshal().Should().BeEquivalentTo(packet, Configure); } IPostprocessComposer BuildPacket() where T : IHaveHeader, new() @@ -388,4 +390,7 @@ public void ReadTimeTrialDataPacket() return fixture.Build() .With(x => x.Header, header); } + + static EquivalencyOptions Configure(EquivalencyOptions options) + => options.IncludingInternalFields(); } diff --git a/F1Game.UDP/Events/EventDetails.cs b/F1Game.UDP/Events/EventDetails.cs index 1aaba90..b57ae4e 100644 --- a/F1Game.UDP/Events/EventDetails.cs +++ b/F1Game.UDP/Events/EventDetails.cs @@ -1,51 +1,190 @@ namespace F1Game.UDP.Events; [StructLayout(LayoutKind.Explicit, Pack = 1, Size = 26)] -public readonly record struct EventDetails +public readonly record struct EventDetails : IByteWritable { [field: FieldOffset(0)] public EventType EventType { get; init; } // Event string code, see below 4 chars [field: FieldOffset(4)] - public ButtonsEvent ButtonsEvent { get; init; } + private readonly ButtonsEvent buttonsEvent; [field: FieldOffset(4)] - public DriveThroughPenaltyServedEvent DriveThroughPenaltyServedEvent { get; init; } + private readonly DriveThroughPenaltyServedEvent driveThroughPenaltyServedEvent; [field: FieldOffset(4)] - public FastestLapEvent FastestLapEvent { get; init; } + private readonly FastestLapEvent fastestLapEvent; [field: FieldOffset(4)] - public FlashbackEvent FlashbackEvent { get; init; } + private readonly FlashbackEvent flashbackEvent; [field: FieldOffset(4)] - public OvertakeEvent OvertakeEvent { get; init; } + private readonly OvertakeEvent overtakeEvent; [field: FieldOffset(4)] - public PenaltyEvent PenaltyEvent { get; init; } + private readonly PenaltyEvent penaltyEvent; [field: FieldOffset(4)] - public RaceWinnerEvent RaceWinnerEvent { get; init; } + private readonly RaceWinnerEvent raceWinnerEvent; [field: FieldOffset(4)] - public RetirementEvent RetirementEvent { get; init; } + private readonly RetirementEvent retirementEvent; [field: FieldOffset(4)] - public SpeedTrapEvent SpeedTrapEvent { get; init; } + private readonly SpeedTrapEvent speedTrapEvent; [field: FieldOffset(4)] - public StartLightsEvent StartLightsEvent { get; init; } + private readonly StartLightsEvent startLightsEvent; [field: FieldOffset(4)] - public StopGoPenaltyServedEvent StopGoPenaltyServedEvent { get; init; } + private readonly StopGoPenaltyServedEvent stopGoPenaltyServedEvent; [field: FieldOffset(4)] - public TeamMateInPitsEvent TeamMateInPitsEvent { get; init; } + private readonly TeamMateInPitsEvent teamMateInPitsEvent; [field: FieldOffset(4)] - public CollisionEvent CollisionEvent { get; init; } + private readonly CollisionEvent collisionEvent; [field: FieldOffset(4)] - public SafetyCarEvent SafetyCarEvent { get; init; } + private readonly SafetyCarEvent safetyCarEvent; - public static implicit operator EventDetails(ButtonsEvent buttonsEvent) => new() { EventType = EventType.ButtonStatus, ButtonsEvent = buttonsEvent }; - public static implicit operator EventDetails(DriveThroughPenaltyServedEvent driveThroughPenaltyServedEvent) => new() { EventType = EventType.DriveThroughServed, DriveThroughPenaltyServedEvent = driveThroughPenaltyServedEvent }; - public static implicit operator EventDetails(FastestLapEvent fastestLapEvent) => new() { EventType = EventType.FastestLap, FastestLapEvent = fastestLapEvent }; - public static implicit operator EventDetails(FlashbackEvent flashbackEvent) => new() { EventType = EventType.Flashback, FlashbackEvent = flashbackEvent }; - public static implicit operator EventDetails(OvertakeEvent overtakeEvent) => new() { EventType = EventType.Overtake, OvertakeEvent = overtakeEvent }; - public static implicit operator EventDetails(PenaltyEvent penaltyEvent) => new() { EventType = EventType.PenaltyIssued, PenaltyEvent = penaltyEvent }; - public static implicit operator EventDetails(RaceWinnerEvent raceWinnerEvent) => new() { EventType = EventType.RaceWinner, RaceWinnerEvent = raceWinnerEvent }; - public static implicit operator EventDetails(RetirementEvent retirementEvent) => new() { EventType = EventType.Retirement, RetirementEvent = retirementEvent }; - public static implicit operator EventDetails(SpeedTrapEvent speedTrapEvent) => new() { EventType = EventType.SpeedTrapTriggered, SpeedTrapEvent = speedTrapEvent }; - public static implicit operator EventDetails(StartLightsEvent startLightsEvent) => new() { EventType = EventType.StartLights, StartLightsEvent = startLightsEvent }; - public static implicit operator EventDetails(StopGoPenaltyServedEvent stopGoPenaltyServedEvent) => new() { EventType = EventType.StopGoServed, StopGoPenaltyServedEvent = stopGoPenaltyServedEvent }; - public static implicit operator EventDetails(TeamMateInPitsEvent teamMateInPitsEvent) => new() { EventType = EventType.TeamMateInPits, TeamMateInPitsEvent = teamMateInPitsEvent }; - public static implicit operator EventDetails(CollisionEvent collisionEvent) => new() { EventType = EventType.Collision, CollisionEvent = collisionEvent }; - public static implicit operator EventDetails(SafetyCarEvent safetyCarEvent) => new() { EventType = EventType.SafetyCar, SafetyCarEvent = safetyCarEvent }; + public EventDetails(ButtonsEvent buttonsEvent) => (this.buttonsEvent, EventType) = (buttonsEvent, EventType.ButtonStatus); + public EventDetails(DriveThroughPenaltyServedEvent driveThroughPenaltyServedEvent) => (this.driveThroughPenaltyServedEvent, EventType) = (driveThroughPenaltyServedEvent, EventType.DriveThroughServed); + public EventDetails(FastestLapEvent fastestLapEvent) => (this.fastestLapEvent, EventType) = (fastestLapEvent, EventType.FastestLap); + public EventDetails(FlashbackEvent flashbackEvent) => (this.flashbackEvent, EventType) = (flashbackEvent, EventType.Flashback); + public EventDetails(OvertakeEvent overtakeEvent) => (this.overtakeEvent, EventType) = (overtakeEvent, EventType.Overtake); + public EventDetails(PenaltyEvent penaltyEvent) => (this.penaltyEvent, EventType) = (penaltyEvent, EventType.PenaltyIssued); + public EventDetails(RaceWinnerEvent raceWinnerEvent) => (this.raceWinnerEvent, EventType) = (raceWinnerEvent, EventType.RaceWinner); + public EventDetails(RetirementEvent retirementEvent) => (this.retirementEvent, EventType) = (retirementEvent, EventType.Retirement); + public EventDetails(SpeedTrapEvent speedTrapEvent) => (this.speedTrapEvent, EventType) = (speedTrapEvent, EventType.SpeedTrapTriggered); + public EventDetails(StartLightsEvent startLightsEvent) => (this.startLightsEvent, EventType) = (startLightsEvent, EventType.StartLights); + public EventDetails(StopGoPenaltyServedEvent stopGoPenaltyServedEvent) => (this.stopGoPenaltyServedEvent, EventType) = (stopGoPenaltyServedEvent, EventType.StopGoServed); + public EventDetails(TeamMateInPitsEvent teamMateInPitsEvent) => (this.teamMateInPitsEvent, EventType) = (teamMateInPitsEvent, EventType.TeamMateInPits); + public EventDetails(CollisionEvent collisionEvent) => (this.collisionEvent, EventType) = (collisionEvent, EventType.Collision); + public EventDetails(SafetyCarEvent safetyCarEvent) => (this.safetyCarEvent, EventType) = (safetyCarEvent, EventType.SafetyCar); + + public bool TryGetButtonsEvent(out ButtonsEvent buttonsEvent) + { + var isRightEvent = EventType == EventType.ButtonStatus; + buttonsEvent = isRightEvent ? this.buttonsEvent : default; + return isRightEvent; + } + + public bool TryGetDriveThroughPenaltyServedEvent(out DriveThroughPenaltyServedEvent driveThroughPenaltyServedEvent) + { + var isRightEvent = EventType == EventType.DriveThroughServed; + driveThroughPenaltyServedEvent = isRightEvent ? this.driveThroughPenaltyServedEvent : default; + return isRightEvent; + } + + public bool TryGetFastestLapEvent(out FastestLapEvent fastestLapEvent) + { + var isRightEvent = EventType == EventType.FastestLap; + fastestLapEvent = isRightEvent ? this.fastestLapEvent : default; + return isRightEvent; + } + + public bool TryGetFlashbackEvent(out FlashbackEvent flashbackEvent) + { + var isRightEvent = EventType == EventType.Flashback; + flashbackEvent = isRightEvent ? this.flashbackEvent : default; + return isRightEvent; + } + + public bool TryGetOvertakeEvent(out OvertakeEvent overtakeEvent) + { + var isRightEvent = EventType == EventType.Overtake; + overtakeEvent = isRightEvent ? this.overtakeEvent : default; + return isRightEvent; + } + + public bool TryGetPenaltyEvent(out PenaltyEvent penaltyEvent) + { + var isRightEvent = EventType == EventType.PenaltyIssued; + penaltyEvent = isRightEvent ? this.penaltyEvent : default; + return isRightEvent; + } + + public bool TryGetRaceWinnerEvent(out RaceWinnerEvent raceWinnerEvent) + { + var isRightEvent = EventType == EventType.RaceWinner; + raceWinnerEvent = isRightEvent ? this.raceWinnerEvent : default; + return isRightEvent; + } + + public bool TryGetRetirementEvent(out RetirementEvent retirementEvent) + { + var isRightEvent = EventType == EventType.Retirement; + retirementEvent = isRightEvent ? this.retirementEvent : default; + return isRightEvent; + } + + public bool TryGetSpeedTrapEvent(out SpeedTrapEvent speedTrapEvent) + { + var isRightEvent = EventType == EventType.SpeedTrapTriggered; + speedTrapEvent = isRightEvent ? this.speedTrapEvent : default; + return isRightEvent; + } + + public bool TryGetStartLightsEvent(out StartLightsEvent startLightsEvent) + { + var isRightEvent = EventType == EventType.StartLights; + startLightsEvent = isRightEvent ? this.startLightsEvent : default; + return isRightEvent; + } + + public bool TryGetStopGoPenaltyServedEvent(out StopGoPenaltyServedEvent stopGoPenaltyServedEvent) + { + var isRightEvent = EventType == EventType.StopGoServed; + stopGoPenaltyServedEvent = isRightEvent ? this.stopGoPenaltyServedEvent : default; + return isRightEvent; + } + + public bool TryGetTeamMateInPitsEvent(out TeamMateInPitsEvent teamMateInPitsEvent) + { + var isRightEvent = EventType == EventType.TeamMateInPits; + teamMateInPitsEvent = isRightEvent ? this.teamMateInPitsEvent : default; + return isRightEvent; + } + + public bool TryGetCollisionEvent(out CollisionEvent collisionEvent) + { + var isRightEvent = EventType == EventType.Collision; + collisionEvent = isRightEvent ? this.collisionEvent : default; + return isRightEvent; + } + + public bool TryGetSafetyCarEvent(out SafetyCarEvent safetyCarEvent) + { + var isRightEvent = EventType == EventType.SafetyCar; + safetyCarEvent = isRightEvent ? this.safetyCarEvent : default; + return isRightEvent; + } + + void IByteWritable.WriteBytes(ref BytesWriter writer) + { + writer.WriteEnum(EventType); + + IByteWritable? byteWritable = EventType switch + { + EventType.FastestLap => fastestLapEvent, + EventType.Retirement => retirementEvent, + EventType.TeamMateInPits => teamMateInPitsEvent, + EventType.RaceWinner => raceWinnerEvent, + EventType.PenaltyIssued => penaltyEvent, + EventType.SpeedTrapTriggered => speedTrapEvent, + EventType.StartLights => startLightsEvent, + EventType.DriveThroughServed => driveThroughPenaltyServedEvent, + EventType.StopGoServed => stopGoPenaltyServedEvent, + EventType.Flashback => flashbackEvent, + EventType.ButtonStatus => buttonsEvent, + EventType.Overtake => overtakeEvent, + EventType.SafetyCar => safetyCarEvent, + EventType.Collision => collisionEvent, + _ => null, + }; + + byteWritable?.WriteBytes(ref writer); + } + + public static implicit operator EventDetails(ButtonsEvent buttonsEvent) => new(buttonsEvent); + public static implicit operator EventDetails(DriveThroughPenaltyServedEvent driveThroughPenaltyServedEvent) => new(driveThroughPenaltyServedEvent); + public static implicit operator EventDetails(FastestLapEvent fastestLapEvent) => new(fastestLapEvent); + public static implicit operator EventDetails(FlashbackEvent flashbackEvent) => new(flashbackEvent); + public static implicit operator EventDetails(OvertakeEvent overtakeEvent) => new(overtakeEvent); + public static implicit operator EventDetails(PenaltyEvent penaltyEvent) => new(penaltyEvent); + public static implicit operator EventDetails(RaceWinnerEvent raceWinnerEvent) => new(raceWinnerEvent); + public static implicit operator EventDetails(RetirementEvent retirementEvent) => new(retirementEvent); + public static implicit operator EventDetails(SpeedTrapEvent speedTrapEvent) => new(speedTrapEvent); + public static implicit operator EventDetails(StartLightsEvent startLightsEvent) => new(startLightsEvent); + public static implicit operator EventDetails(StopGoPenaltyServedEvent stopGoPenaltyServedEvent) => new(stopGoPenaltyServedEvent); + public static implicit operator EventDetails(TeamMateInPitsEvent teamMateInPitsEvent) => new(teamMateInPitsEvent); + public static implicit operator EventDetails(CollisionEvent collisionEvent) => new(collisionEvent); + public static implicit operator EventDetails(SafetyCarEvent safetyCarEvent) => new(safetyCarEvent); } diff --git a/F1Game.UDP/F1Game.UDP.csproj b/F1Game.UDP/F1Game.UDP.csproj index e46b719..1ed4bae 100644 --- a/F1Game.UDP/F1Game.UDP.csproj +++ b/F1Game.UDP/F1Game.UDP.csproj @@ -1,7 +1,7 @@  - net8.0 + net8.0;net9.0 enable enable latest diff --git a/F1Game.UDP/Packets/EventDataPacket.cs b/F1Game.UDP/Packets/EventDataPacket.cs index 4a406d0..2bfbc4b 100644 --- a/F1Game.UDP/Packets/EventDataPacket.cs +++ b/F1Game.UDP/Packets/EventDataPacket.cs @@ -8,8 +8,8 @@ public readonly record struct EventDataPacket() : IByteParsable { public static int Size => 45; - public PacketHeader Header { get; init; } = PacketHeader.Empty; // Header - public EventDetails EventDetails { get; init; } // Event details - should be interpreted differently + public PacketHeader Header { get; init; } = PacketHeader.Empty; + public EventDetails EventDetails { get; init; } static EventDataPacket IByteParsable.Parse(ref BytesReader reader) { @@ -50,27 +50,6 @@ static EventDataPacket IByteParsable.Parse(ref BytesReader read void IByteWritable.WriteBytes(ref BytesWriter writer) { writer.Write(Header); - writer.WriteEnum(EventDetails.EventType); - - IByteWritable? byteWritable = EventDetails.EventType switch - { - EventType.FastestLap => EventDetails.FastestLapEvent, - EventType.Retirement => EventDetails.RetirementEvent, - EventType.TeamMateInPits => EventDetails.TeamMateInPitsEvent, - EventType.RaceWinner => EventDetails.RaceWinnerEvent, - EventType.PenaltyIssued => EventDetails.PenaltyEvent, - EventType.SpeedTrapTriggered => EventDetails.SpeedTrapEvent, - EventType.StartLights => EventDetails.StartLightsEvent, - EventType.DriveThroughServed => EventDetails.DriveThroughPenaltyServedEvent, - EventType.StopGoServed => EventDetails.StopGoPenaltyServedEvent, - EventType.Flashback => EventDetails.FlashbackEvent, - EventType.ButtonStatus => EventDetails.ButtonsEvent, - EventType.Overtake => EventDetails.OvertakeEvent, - EventType.SafetyCar => EventDetails.SafetyCarEvent, - EventType.Collision => EventDetails.CollisionEvent, - _ => null, - }; - if (byteWritable is not null) - writer.Write(byteWritable); + writer.Write(EventDetails); } } diff --git a/F1Game.UDP/Packets/UnionPacket.cs b/F1Game.UDP/Packets/UnionPacket.cs index 2f181dd..82d015b 100644 --- a/F1Game.UDP/Packets/UnionPacket.cs +++ b/F1Game.UDP/Packets/UnionPacket.cs @@ -13,74 +13,195 @@ namespace F1Game.UDP.Packets; [field: FieldOffset(0)] public PacketHeader Header { get; init; } [field: FieldOffset(0)] - public CarDamageDataPacket CarDamageDataPacket { get; init; } + private readonly CarDamageDataPacket carDamageDataPacket; [field: FieldOffset(0)] - public CarSetupDataPacket CarSetupDataPacket { get; init; } + private readonly CarSetupDataPacket carSetupDataPacket; [field: FieldOffset(0)] - public CarStatusDataPacket CarStatusDataPacket { get; init; } + private readonly CarStatusDataPacket carStatusDataPacket; [field: FieldOffset(0)] - public CarTelemetryDataPacket CarTelemetryDataPacket { get; init; } + private readonly CarTelemetryDataPacket carTelemetryDataPacket; [field: FieldOffset(0)] - public EventDataPacket EventDataPacket { get; init; } + private readonly EventDataPacket eventDataPacket; [field: FieldOffset(0)] - public FinalClassificationDataPacket FinalClassificationDataPacket { get; init; } + private readonly FinalClassificationDataPacket finalClassificationDataPacket; [field: FieldOffset(0)] - public LapDataPacket LapDataPacket { get; init; } + private readonly LapDataPacket lapDataPacket; [field: FieldOffset(0)] - public LobbyInfoDataPacket LobbyInfoDataPacket { get; init; } + private readonly LobbyInfoDataPacket lobbyInfoDataPacket; [field: FieldOffset(0)] - public MotionDataPacket MotionDataPacket { get; init; } + private readonly MotionDataPacket motionDataPacket; [field: FieldOffset(0)] - public ParticipantsDataPacket ParticipantsDataPacket { get; init; } + private readonly ParticipantsDataPacket participantsDataPacket; [field: FieldOffset(0)] - public SessionDataPacket SessionDataPacket { get; init; } + private readonly SessionDataPacket sessionDataPacket; [field: FieldOffset(0)] - public SessionHistoryDataPacket SessionHistoryDataPacket { get; init; } + private readonly SessionHistoryDataPacket sessionHistoryDataPacket; [field: FieldOffset(0)] - public TyreSetsDataPacket TyreSetsDataPacket { get; init; } + private readonly TyreSetsDataPacket tyreSetsDataPacket; [field: FieldOffset(0)] - public MotionExDataPacket MotionExDataPacket { get; init; } + private readonly MotionExDataPacket motionExDataPacket; [field: FieldOffset(0)] - public TimeTrialDataPacket TimeTrialDataPacket { get; init; } + private readonly TimeTrialDataPacket timeTrialDataPacket; + + public UnionPacket(CarDamageDataPacket carDamageDataPacket) => (this.carDamageDataPacket, PacketType) = (carDamageDataPacket, PacketType.CarDamage); + public UnionPacket(CarSetupDataPacket carSetupDataPacket) => (this.carSetupDataPacket, PacketType) = (carSetupDataPacket, PacketType.CarSetups); + public UnionPacket(CarStatusDataPacket carStatusDataPacket) => (this.carStatusDataPacket, PacketType) = (carStatusDataPacket, PacketType.CarStatus); + public UnionPacket(CarTelemetryDataPacket carTelemetryDataPacket) => (this.carTelemetryDataPacket, PacketType) = (carTelemetryDataPacket, PacketType.CarTelemetry); + public UnionPacket(EventDataPacket eventDataPacket) => (this.eventDataPacket, PacketType) = (eventDataPacket, PacketType.Event); + public UnionPacket(FinalClassificationDataPacket finalClassificationDataPacket) => (this.finalClassificationDataPacket, PacketType) = (finalClassificationDataPacket, PacketType.FinalClassification); + public UnionPacket(LapDataPacket lapDataPacket) => (this.lapDataPacket, PacketType) = (lapDataPacket, PacketType.LapData); + public UnionPacket(LobbyInfoDataPacket lobbyInfoDataPacket) => (this.lobbyInfoDataPacket, PacketType) = (lobbyInfoDataPacket, PacketType.LobbyInfo); + public UnionPacket(MotionDataPacket motionDataPacket) => (this.motionDataPacket, PacketType) = (motionDataPacket, PacketType.Motion); + public UnionPacket(ParticipantsDataPacket participantsDataPacket) => (this.participantsDataPacket, PacketType) = (participantsDataPacket, PacketType.Participants); + public UnionPacket(SessionDataPacket sessionDataPacket) => (this.sessionDataPacket, PacketType) = (sessionDataPacket, PacketType.Session); + public UnionPacket(SessionHistoryDataPacket sessionHistoryDataPacket) => (this.sessionHistoryDataPacket, PacketType) = (sessionHistoryDataPacket, PacketType.SessionHistory); + public UnionPacket(TyreSetsDataPacket tyreSetsDataPacket) => (this.tyreSetsDataPacket, PacketType) = (tyreSetsDataPacket, PacketType.TyreSets); + public UnionPacket(MotionExDataPacket motionExDataPacket) => (this.motionExDataPacket, PacketType) = (motionExDataPacket, PacketType.MotionEx); + public UnionPacket(TimeTrialDataPacket timeTrialDataPacket) => (this.timeTrialDataPacket, PacketType) = (timeTrialDataPacket, PacketType.TimeTrial); + + public bool TryGetCarDamageDataPacket(out CarDamageDataPacket carDamageDataPacket) + { + var isRightPacket = PacketType == PacketType.CarDamage; + carDamageDataPacket = isRightPacket ? this.carDamageDataPacket : default; + return isRightPacket; + } + + public bool TryGetCarSetupDataPacket(out CarSetupDataPacket carSetupDataPacket) + { + var isRightPacket = PacketType == PacketType.CarSetups; + carSetupDataPacket = isRightPacket ? this.carSetupDataPacket : default; + return isRightPacket; + } + + public bool TryGetCarStatusDataPacket(out CarStatusDataPacket carStatusDataPacket) + { + var isRightPacket = PacketType == PacketType.CarStatus; + carStatusDataPacket = isRightPacket ? this.carStatusDataPacket : default; + return isRightPacket; + } + + public bool TryGetCarTelemetryDataPacket(out CarTelemetryDataPacket carTelemetryDataPacket) + { + var isRightPacket = PacketType == PacketType.CarTelemetry; + carTelemetryDataPacket = isRightPacket ? this.carTelemetryDataPacket : default; + return isRightPacket; + } + + public bool TryGetEventDataPacket(out EventDataPacket eventDataPacket) + { + var isRightPacket = PacketType == PacketType.Event; + eventDataPacket = isRightPacket ? this.eventDataPacket : default; + return isRightPacket; + } + + public bool TryGetFinalClassificationDataPacket(out FinalClassificationDataPacket finalClassificationDataPacket) + { + var isRightPacket = PacketType == PacketType.FinalClassification; + finalClassificationDataPacket = isRightPacket ? this.finalClassificationDataPacket : default; + return isRightPacket; + } + + public bool TryGetLapDataPacket(out LapDataPacket lapDataPacket) + { + var isRightPacket = PacketType == PacketType.LapData; + lapDataPacket = isRightPacket ? this.lapDataPacket : default; + return isRightPacket; + } + + public bool TryGetLobbyInfoDataPacket(out LobbyInfoDataPacket lobbyInfoDataPacket) + { + var isRightPacket = PacketType == PacketType.LobbyInfo; + lobbyInfoDataPacket = isRightPacket ? this.lobbyInfoDataPacket : default; + return isRightPacket; + } + + public bool TryGetMotionDataPacket(out MotionDataPacket motionDataPacket) + { + var isRightPacket = PacketType == PacketType.Motion; + motionDataPacket = isRightPacket ? this.motionDataPacket : default; + return isRightPacket; + } + + public bool TryGetParticipantsDataPacket(out ParticipantsDataPacket participantsDataPacket) + { + var isRightPacket = PacketType == PacketType.Participants; + participantsDataPacket = isRightPacket ? this.participantsDataPacket : default; + return isRightPacket; + } + + public bool TryGetSessionDataPacket(out SessionDataPacket sessionDataPacket) + { + var isRightPacket = PacketType == PacketType.Session; + sessionDataPacket = isRightPacket ? this.sessionDataPacket : default; + return isRightPacket; + } + + public bool TryGetSessionHistoryDataPacket(out SessionHistoryDataPacket sessionHistoryDataPacket) + { + var isRightPacket = PacketType == PacketType.SessionHistory; + sessionHistoryDataPacket = isRightPacket ? this.sessionHistoryDataPacket : default; + return isRightPacket; + } + + public bool TryGetTyreSetsDataPacket(out TyreSetsDataPacket tyreSetsDataPacket) + { + var isRightPacket = PacketType == PacketType.TyreSets; + tyreSetsDataPacket = isRightPacket ? this.tyreSetsDataPacket : default; + return isRightPacket; + } + + public bool TryGetMotionExDataPacket(out MotionExDataPacket motionExDataPacket) + { + var isRightPacket = PacketType == PacketType.MotionEx; + motionExDataPacket = isRightPacket ? this.motionExDataPacket : default; + return isRightPacket; + } + + public bool TryGetTimeTrialDataPacket(out TimeTrialDataPacket timeTrialDataPacket) + { + var isRightPacket = PacketType == PacketType.TimeTrial; + timeTrialDataPacket = isRightPacket ? this.timeTrialDataPacket : default; + return isRightPacket; + } void IByteWritable.WriteBytes(ref BytesWriter writer) { IByteWritable byteWritable = PacketType switch { - PacketType.CarDamage => CarDamageDataPacket, - PacketType.CarSetups => CarSetupDataPacket, - PacketType.CarStatus => CarStatusDataPacket, - PacketType.CarTelemetry => CarTelemetryDataPacket, - PacketType.Event => EventDataPacket, - PacketType.FinalClassification => FinalClassificationDataPacket, - PacketType.MotionEx => MotionExDataPacket, - PacketType.LapData => LapDataPacket, - PacketType.LobbyInfo => LobbyInfoDataPacket, - PacketType.Motion => MotionDataPacket, - PacketType.Participants => ParticipantsDataPacket, - PacketType.Session => SessionDataPacket, - PacketType.SessionHistory => SessionHistoryDataPacket, - PacketType.TyreSets => TyreSetsDataPacket, - PacketType.TimeTrial => TimeTrialDataPacket, + PacketType.CarDamage => carDamageDataPacket, + PacketType.CarSetups => carSetupDataPacket, + PacketType.CarStatus => carStatusDataPacket, + PacketType.CarTelemetry => carTelemetryDataPacket, + PacketType.Event => eventDataPacket, + PacketType.FinalClassification => finalClassificationDataPacket, + PacketType.MotionEx => motionExDataPacket, + PacketType.LapData => lapDataPacket, + PacketType.LobbyInfo => lobbyInfoDataPacket, + PacketType.Motion => motionDataPacket, + PacketType.Participants => participantsDataPacket, + PacketType.Session => sessionDataPacket, + PacketType.SessionHistory => sessionHistoryDataPacket, + PacketType.TyreSets => tyreSetsDataPacket, + PacketType.TimeTrial => timeTrialDataPacket, _ => throw new UnreachableException() }; writer.Write(byteWritable); } - public static implicit operator UnionPacket(CarDamageDataPacket packet) => new() { CarDamageDataPacket = packet, PacketType = PacketType.CarDamage }; - public static implicit operator UnionPacket(CarSetupDataPacket packet) => new() { CarSetupDataPacket = packet, PacketType = PacketType.CarSetups }; - public static implicit operator UnionPacket(CarStatusDataPacket packet) => new() { CarStatusDataPacket = packet, PacketType = PacketType.CarStatus }; - public static implicit operator UnionPacket(CarTelemetryDataPacket packet) => new() { CarTelemetryDataPacket = packet, PacketType = PacketType.CarTelemetry }; - public static implicit operator UnionPacket(EventDataPacket packet) => new() { EventDataPacket = packet, PacketType = PacketType.Event }; - public static implicit operator UnionPacket(FinalClassificationDataPacket packet) => new() { FinalClassificationDataPacket = packet, PacketType = PacketType.FinalClassification }; - public static implicit operator UnionPacket(LapDataPacket packet) => new() { LapDataPacket = packet, PacketType = PacketType.LapData }; - public static implicit operator UnionPacket(LobbyInfoDataPacket packet) => new() { LobbyInfoDataPacket = packet, PacketType = PacketType.LobbyInfo }; - public static implicit operator UnionPacket(MotionDataPacket packet) => new() { MotionDataPacket = packet, PacketType = PacketType.Motion }; - public static implicit operator UnionPacket(ParticipantsDataPacket packet) => new() { ParticipantsDataPacket = packet, PacketType = PacketType.Participants }; - public static implicit operator UnionPacket(SessionDataPacket packet) => new() { SessionDataPacket = packet, PacketType = PacketType.Session }; - public static implicit operator UnionPacket(SessionHistoryDataPacket packet) => new() { SessionHistoryDataPacket = packet, PacketType = PacketType.SessionHistory }; - public static implicit operator UnionPacket(TyreSetsDataPacket packet) => new() { TyreSetsDataPacket = packet, PacketType = PacketType.TyreSets }; - public static implicit operator UnionPacket(MotionExDataPacket packet) => new() { MotionExDataPacket = packet, PacketType = PacketType.MotionEx }; - public static implicit operator UnionPacket(TimeTrialDataPacket packet) => new() { TimeTrialDataPacket = packet, PacketType = PacketType.TimeTrial }; + public static implicit operator UnionPacket(CarDamageDataPacket packet) => new(packet); + public static implicit operator UnionPacket(CarSetupDataPacket packet) => new(packet); + public static implicit operator UnionPacket(CarStatusDataPacket packet) => new(packet); + public static implicit operator UnionPacket(CarTelemetryDataPacket packet) => new(packet); + public static implicit operator UnionPacket(EventDataPacket packet) => new(packet); + public static implicit operator UnionPacket(FinalClassificationDataPacket packet) => new(packet); + public static implicit operator UnionPacket(LapDataPacket packet) => new(packet); + public static implicit operator UnionPacket(LobbyInfoDataPacket packet) => new(packet); + public static implicit operator UnionPacket(MotionDataPacket packet) => new(packet); + public static implicit operator UnionPacket(ParticipantsDataPacket packet) => new(packet); + public static implicit operator UnionPacket(SessionDataPacket packet) => new(packet); + public static implicit operator UnionPacket(SessionHistoryDataPacket packet) => new(packet); + public static implicit operator UnionPacket(TyreSetsDataPacket packet) => new(packet); + public static implicit operator UnionPacket(MotionExDataPacket packet) => new(packet); + public static implicit operator UnionPacket(TimeTrialDataPacket packet) => new(packet); } diff --git a/README.md b/README.md index f6942c3..6b737ca 100644 --- a/README.md +++ b/README.md @@ -33,31 +33,35 @@ The `ToPacket` method returns a `UnionPacket` struct, which has properties for d - `TimeTrialDataPacket` - `TyreSetsDataPacket` -You can access the specific packet data by accessing the corresponding property of the `UnionPacket` struct, you should check what packet type it is first using `PacketType` property. +First, check the packet type using the `PacketType` property. Then, access the specific packet data by calling the corresponding `TryGet` method of the `UnionPacket` struct. ``` using F1Game.UDP; UnionPacket packet = arrayOfBytes.ToPacket(); -if (packet.PacketType == PacketType.CarTelemetry) +if (packet.TryGetCarTelemetryDataPacket(out var carTelemetryData)) { - CarTelemetryDataPacket carTelemetryData = packet.CarTelemetryData; // Access car telemetry data } switch (packet.PacketType) { - case PacketType.CarTelemetry: - CarTelemetryDataPacket carTelemetryData = packet.CarTelemetryData; + case PacketType.CarTelemetry when packet.TryGetCarTelemetryDataPacket(out var carTelemetryData): // Access car telemetry data break; - case PacketType.CarStatus: - CarStatusDataPacket carStatusData = packet.CarStatusData; + case PacketType.CarStatus when packet.TryGetCarStatusDataPacket(out var carStatusData): // Access car status data break; // Add other cases for different packet types } + +var someResult = packet.PacketType switch +{ + PacketType.CarTelemetry when packet.TryGetCarTelemetryDataPacket(out var carTelemetryData) => // Access car telemetry data, + PacketType.CarStatus when packet.TryGetCarStatusDataPacket(out var carStatusData) => // Access car status data, + _ => "default" +}; ``` # Benchmarks