From 16fbd2f5f538cfd5df4cbf1846ce6630f2616c26 Mon Sep 17 00:00:00 2001 From: Ciaran Fisher Date: Wed, 3 Jun 2020 20:12:50 +0100 Subject: [PATCH 01/23] Added Transmission ID bytes --- DCS-SR-Client/Network/UDPVoiceHandler.cs | 3 +- DCS-SR-Common/Network/UDPVoicePacket.cs | 19 +++++-- .../Network/UDPVoicePacketTests.cs | 54 +++++++++++-------- 3 files changed, 50 insertions(+), 26 deletions(-) diff --git a/DCS-SR-Client/Network/UDPVoiceHandler.cs b/DCS-SR-Client/Network/UDPVoiceHandler.cs index 24f50ac35..713ab4584 100644 --- a/DCS-SR-Client/Network/UDPVoiceHandler.cs +++ b/DCS-SR-Client/Network/UDPVoiceHandler.cs @@ -804,7 +804,8 @@ public bool Send(byte[] bytes, int len) UnitId = _clientStateSingleton.DcsPlayerRadioInfo.unitId, Encryptions = encryptions.ToArray(), Modulations = modulations.ToArray(), - PacketNumber = _packetNumber++ + PacketNumber = _packetNumber++, + TransmissionBytes = _guidAsciiBytes }; var encodedUdpVoicePacket = udpVoicePacket.EncodePacket(); diff --git a/DCS-SR-Common/Network/UDPVoicePacket.cs b/DCS-SR-Common/Network/UDPVoicePacket.cs index 55e2dfeb0..44687d6fc 100644 --- a/DCS-SR-Common/Network/UDPVoicePacket.cs +++ b/DCS-SR-Common/Network/UDPVoicePacket.cs @@ -20,7 +20,8 @@ namespace Ciribob.DCS.SimpleRadio.Standalone.Common.Network * - FIXED SEGMENT * UInt UnitId - 4 bytes * UInt64 PacketId - 8 bytes - * Bytes / ASCII String GUID - 22 bytes + * Bytes / ASCII String TRANSMISSION GUID - 22 bytes used for transmission relay + * Bytes / ASCII String CLIENT GUID - 22 bytes */ public class UDPVoicePacket @@ -42,6 +43,7 @@ public class UDPVoicePacket public static readonly int FixedPacketLength = sizeof(uint) // UInt UnitId - 4 bytes + sizeof(ulong) // UInt64 PacketId - 8 bytes + + GuidLength // Bytes / ASCII String Transmission GUID - 22 bytes + GuidLength; // Bytes / ASCII String GUID - 22 bytes // HEADER SEGMENT @@ -70,6 +72,9 @@ public class UDPVoicePacket public uint UnitId { get; set; } public byte[] GuidBytes { get; set; } public string Guid { get; set; } + + public byte[] TransmissionBytes { get; set; } + public string TransmissionGuid { get; set; } public ulong PacketNumber { get; set; } public byte[] EncodePacket() @@ -170,6 +175,9 @@ public byte[] EncodePacket() combinedBytes[fixedSegmentOffset + 10] = packetNumber[6]; combinedBytes[fixedSegmentOffset + 11] = packetNumber[7]; + //Copy Transmission nearly at the end - just before the clientGUID + Buffer.BlockCopy(TransmissionBytes, 0, combinedBytes, totalPacketLength - (GuidLength + GuidLength), GuidLength); + // Copy client GUID to end of packet Buffer.BlockCopy(GuidBytes, 0, combinedBytes, totalPacketLength - GuidLength, GuidLength); @@ -180,12 +188,14 @@ public static UDPVoicePacket DecodeVoicePacket(byte[] encodedOpusAudio, bool dec { try { - - // Last 22 bytes of packet are always the client GUID var receivingGuid = Encoding.ASCII.GetString( encodedOpusAudio, encodedOpusAudio.Length - GuidLength, GuidLength); + //Copy Transmission nearly at the end - just before the client GUID + var transmissionGuid = Encoding.ASCII.GetString( + encodedOpusAudio, encodedOpusAudio.Length - (GuidLength + GuidLength), GuidLength); + var packetLength = BitConverter.ToUInt16(encodedOpusAudio, 0); var ecnAudio1 = BitConverter.ToUInt16(encodedOpusAudio, 2); @@ -230,7 +240,8 @@ public static UDPVoicePacket DecodeVoicePacket(byte[] encodedOpusAudio, bool dec Encryptions = encryptions, Modulations = modulations, PacketNumber = packetNumber, - PacketLength = packetLength + PacketLength = packetLength, + TransmissionGuid = transmissionGuid }; } catch (Exception ex) diff --git a/DCS-SR-CommonTests/Network/UDPVoicePacketTests.cs b/DCS-SR-CommonTests/Network/UDPVoicePacketTests.cs index 7bf64ec7c..631cc0073 100644 --- a/DCS-SR-CommonTests/Network/UDPVoicePacketTests.cs +++ b/DCS-SR-CommonTests/Network/UDPVoicePacketTests.cs @@ -24,17 +24,18 @@ public void EncodeInitialVoicePacket() UnitId = 1, Encryptions = new byte[] { 0 }, Modulations = new byte[] { 4 }, + TransmissionBytes = Encoding.ASCII.GetBytes("ufYS_WlLVkmFPjqCgxz6GA"), PacketNumber = 1 }; var encodedUdpVoicePacket = udpVoicePacket.EncodePacket(); - Assert.AreEqual(56, udpVoicePacket.PacketLength); - Assert.AreEqual(56, encodedUdpVoicePacket.Length); + Assert.AreEqual(78, udpVoicePacket.PacketLength); + Assert.AreEqual(78, encodedUdpVoicePacket.Length); - var expectedEncodedUdpVoicePacket = new byte[56] { + var expectedEncodedUdpVoicePacket = new byte[78] { // Total packet length - 56, 0, + 78, 0, // Length of audio part 6, 0, // Length of frequencies part @@ -51,6 +52,8 @@ public void EncodeInitialVoicePacket() 1, 0, 0, 0, // Packet ID 1, 0, 0, 0, 0, 0, 0, 0, + // Transmission GUID + 117, 102, 89, 83, 95, 87, 108, 76, 86, 107, 109, 70, 80, 106, 113, 67, 103, 120, 122, 54, 71, 65, // Client GUID 117, 102, 89, 83, 95, 87, 108, 76, 86, 107, 109, 70, 80, 106, 113, 67, 103, 120, 122, 54, 71, 65 }; @@ -61,9 +64,9 @@ public void EncodeInitialVoicePacket() [TestMethod()] public void DecodeInitialVoicePacket() { - var encodedUdpVoicePacket = new byte[56] { + var encodedUdpVoicePacket = new byte[78] { // Total packet length - 56, 0, + 78, 0, // Length of audio part 6, 0, // Length of frequencies part @@ -80,6 +83,8 @@ public void DecodeInitialVoicePacket() 1, 0, 0, 0, // Packet ID 1, 0, 0, 0, 0, 0, 0, 0, + // Transmission GUID + 117, 102, 89, 83, 95, 87, 108, 76, 86, 107, 109, 70, 80, 106, 113, 67, 103, 120, 122, 54, 71, 65, // Client GUID 117, 102, 89, 83, 95, 87, 108, 76, 86, 107, 109, 70, 80, 106, 113, 67, 103, 120, 122, 54, 71, 65 }; @@ -95,6 +100,7 @@ public void DecodeInitialVoicePacket() CollectionAssert.AreEqual(new byte[] { 4 }, udpVoicePacket.Modulations); Assert.AreEqual((ulong)1, udpVoicePacket.PacketNumber); Assert.AreEqual((ushort)56, udpVoicePacket.PacketLength); + Assert.AreEqual("ufYS_WlLVkmFPjqCgxz6GA", udpVoicePacket.TransmissionGuid); } [TestMethod()] @@ -109,17 +115,18 @@ public void EncodeMultipleFrequencyVoicePacket() UnitId = 1, Encryptions = new byte[] { 0, 0, 1 }, Modulations = new byte[] { 0, 1, 0 }, - PacketNumber = 1 + PacketNumber = 1, + TransmissionBytes = Encoding.ASCII.GetBytes("ufYS_WlLVkmFPjqCgxz6GA"), }; - + var encodedUdpVoicePacket = udpVoicePacket.EncodePacket(); - - Assert.AreEqual(76, udpVoicePacket.PacketLength); - Assert.AreEqual(76, encodedUdpVoicePacket.Length); - - var expectedEncodedUdpVoicePacket = new byte[76] { + + Assert.AreEqual(98, udpVoicePacket.PacketLength); + Assert.AreEqual(98, encodedUdpVoicePacket.Length); + + var expectedEncodedUdpVoicePacket = new byte[98] { // Total packet length - 76, 0, + 98, 0, // Length of audio part 6, 0, // Length of frequencies part @@ -148,19 +155,21 @@ public void EncodeMultipleFrequencyVoicePacket() 1, 0, 0, 0, // Packet ID 1, 0, 0, 0, 0, 0, 0, 0, + // Transmission GUID + 117, 102, 89, 83, 95, 87, 108, 76, 86, 107, 109, 70, 80, 106, 113, 67, 103, 120, 122, 54, 71, 65, // Client GUID 117, 102, 89, 83, 95, 87, 108, 76, 86, 107, 109, 70, 80, 106, 113, 67, 103, 120, 122, 54, 71, 65 }; - + CollectionAssert.AreEqual(expectedEncodedUdpVoicePacket, encodedUdpVoicePacket); } - + [TestMethod()] public void DecodeMultipleFrequencyVoicePacket() { - var encodedUdpVoicePacket = new byte[76] { + var encodedUdpVoicePacket = new byte[98] { // Total packet length - 76, 0, + 98, 0, // Length of audio part 6, 0, // Length of frequencies part @@ -189,13 +198,16 @@ public void DecodeMultipleFrequencyVoicePacket() 1, 0, 0, 0, // Packet ID 1, 0, 0, 0, 0, 0, 0, 0, + // Transmission GUID + 117, 102, 89, 83, 95, 87, 108, 76, 86, 107, 109, 70, 80, 106, 113, 67, 103, 120, 122, 54, 71, 65, // Client GUID 117, 102, 89, 83, 95, 87, 108, 76, 86, 107, 109, 70, 80, 106, 113, 67, 103, 120, 122, 54, 71, 65 }; - + var udpVoicePacket = UDPVoicePacket.DecodeVoicePacket(encodedUdpVoicePacket); - + Assert.AreEqual("ufYS_WlLVkmFPjqCgxz6GA", udpVoicePacket.Guid); + Assert.AreEqual("ufYS_WlLVkmFPjqCgxz6GA", udpVoicePacket.TransmissionGuid); CollectionAssert.AreEqual(new byte[] { 0, 1, 2, 3, 4, 5 }, udpVoicePacket.AudioPart1Bytes); Assert.AreEqual(6, udpVoicePacket.AudioPart1Length); CollectionAssert.AreEqual(new double[] { 251000000, 30000000, 251000000 }, udpVoicePacket.Frequencies); @@ -203,7 +215,7 @@ public void DecodeMultipleFrequencyVoicePacket() CollectionAssert.AreEqual(new byte[] { 0, 0, 1 }, udpVoicePacket.Encryptions); CollectionAssert.AreEqual(new byte[] { 0, 1, 0 }, udpVoicePacket.Modulations); Assert.AreEqual((ulong)1, udpVoicePacket.PacketNumber); - Assert.AreEqual((ushort)76, udpVoicePacket.PacketLength); + Assert.AreEqual((ushort)98, udpVoicePacket.PacketLength); } } } \ No newline at end of file From 2ad651cded7ae18519c422eb9298a39fe73f113d Mon Sep 17 00:00:00 2001 From: Ciaran Fisher Date: Sat, 6 Jun 2020 12:35:59 +0100 Subject: [PATCH 02/23] Partial work on transmission relay Basic working GUI - Retransmit set appropriately TODO - Figure out packet number / transmission guid - need to keep the original packet order and use a new opus to decode or there will be cross talk Maybe each transmission just gets a transmission ID? Fixed packet tests Added New Server Setting limiting node hops to stop loops Added multiple jitter buffers per client to account for multiple transmissions --- DCS-SR-Client/Audio/Managers/AudioManager.cs | 4 +- DCS-SR-Client/Audio/Models/ClientAudio.cs | 1 + .../Audio/Models/JitterBufferAudio.cs | 1 + .../Audio/Providers/ClientAudioProvider.cs | 30 ++-- .../Network/DCS/DCSRadioSyncHandler.cs | 2 + DCS-SR-Client/Network/UDPVoiceHandler.cs | 128 ++++++++++++++++-- .../Settings/SynchedServerSettings.cs | 17 +++ .../RadioOverlayWindow/RadioControlGroup.xaml | 128 ++++++++++-------- .../RadioControlGroup.xaml.cs | 21 +++ DCS-SR-Client/Utils/RadioHelper.cs | 17 +++ DCS-SR-Common/DCSState/RadioInformation.cs | 10 +- DCS-SR-Common/Network/UDPVoicePacket.cs | 21 ++- DCS-SR-Common/Setting/ServerSettingsKeys.cs | 4 +- .../Network/UDPVoicePacketTests.cs | 46 +++++-- .../Network/UDPVoiceRouter.cs | 9 ++ 15 files changed, 343 insertions(+), 96 deletions(-) diff --git a/DCS-SR-Client/Audio/Managers/AudioManager.cs b/DCS-SR-Client/Audio/Managers/AudioManager.cs index fb7d96bd0..c58d1c851 100644 --- a/DCS-SR-Client/Audio/Managers/AudioManager.cs +++ b/DCS-SR-Client/Audio/Managers/AudioManager.cs @@ -663,7 +663,7 @@ public void AddClientAudio(ClientAudio audio) client = new ClientAudioProvider(); _clientsBufferedAudio[audio.ClientGuid] = client; - _clientAudioMixer.AddMixerInput(client.SampleProvider); + _clientAudioMixer.AddMixerInput(client.MixingSampleProvider); } client.AddClientAudioSamples(audio); @@ -681,7 +681,7 @@ private void RemoveClientBuffer(SRClient srClient) try { - _clientAudioMixer.RemoveMixerInput(clientAudio.SampleProvider); + _clientAudioMixer.RemoveMixerInput(clientAudio.MixingSampleProvider); } catch (Exception ex) { diff --git a/DCS-SR-Client/Audio/Models/ClientAudio.cs b/DCS-SR-Client/Audio/Models/ClientAudio.cs index 3e1f81e0b..7d8aeb852 100644 --- a/DCS-SR-Client/Audio/Models/ClientAudio.cs +++ b/DCS-SR-Client/Audio/Models/ClientAudio.cs @@ -19,5 +19,6 @@ public class ClientAudio public double RecevingPower { get; set; } public float LineOfSightLoss { get; set; } public ulong PacketNumber { get; set; } + public string TransmissionGuid { get; internal set; } } } \ No newline at end of file diff --git a/DCS-SR-Client/Audio/Models/JitterBufferAudio.cs b/DCS-SR-Client/Audio/Models/JitterBufferAudio.cs index 6b1446a27..90341d0ab 100644 --- a/DCS-SR-Client/Audio/Models/JitterBufferAudio.cs +++ b/DCS-SR-Client/Audio/Models/JitterBufferAudio.cs @@ -5,5 +5,6 @@ public class JitterBufferAudio public byte[] Audio { get; set; } public ulong PacketNumber { get; set; } + public string TransmissionGuid { get; set; } } } \ No newline at end of file diff --git a/DCS-SR-Client/Audio/Providers/ClientAudioProvider.cs b/DCS-SR-Client/Audio/Providers/ClientAudioProvider.cs index 7488ab858..47cf710f3 100644 --- a/DCS-SR-Client/Audio/Providers/ClientAudioProvider.cs +++ b/DCS-SR-Client/Audio/Providers/ClientAudioProvider.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Diagnostics; using Ciribob.DCS.SimpleRadio.Standalone.Client.Audio; using Ciribob.DCS.SimpleRadio.Standalone.Client.Audio.Managers; @@ -37,6 +38,11 @@ public class ClientAudioProvider : AudioProvider //used for comparison public static readonly short FM = Convert.ToInt16((int)RadioInformation.Modulation.FM); + private readonly Dictionary JitterBuffers = new Dictionary(); + + + public MixingSampleProvider MixingSampleProvider { get; } + public ClientAudioProvider() { _filters = new OnlineFilter[2]; @@ -45,10 +51,8 @@ public ClientAudioProvider() _filters[1] = OnlineFilter.CreateBandpass(ImpulseResponse.Finite, AudioManager.INPUT_SAMPLE_RATE, 100, 4500); - JitterBufferProviderInterface = - new JitterBufferProviderInterface(new WaveFormat(AudioManager.INPUT_SAMPLE_RATE, 2)); - - SampleProvider = new Pcm16BitToSampleProvider(JitterBufferProviderInterface); + MixingSampleProvider = new MixingSampleProvider(WaveFormat.CreateIeeeFloatWaveFormat(AudioManager.INPUT_SAMPLE_RATE, 2)); + MixingSampleProvider.ReadFully = true; _decoder = OpusDecoder.Create(AudioManager.INPUT_SAMPLE_RATE, 1); _decoder.ForwardErrorCorrection = false; @@ -72,9 +76,6 @@ public ClientAudioProvider() } } - public JitterBufferProviderInterface JitterBufferProviderInterface { get; } - public Pcm16BitToSampleProvider SampleProvider { get; } - public long LastUpdate { get; private set; } //is it a new transmission? @@ -164,12 +165,20 @@ public void AddClientAudioSamples(ClientAudio audio) _lastReceivedOn = audio.ReceivedRadio; LastUpdate = DateTime.Now.Ticks; - JitterBufferProviderInterface.AddSamples(new JitterBufferAudio + if (!JitterBuffers.TryGetValue(audio.TransmissionGuid, out var jitterBuffer)) + { + jitterBuffer = new JitterBufferProviderInterface(new WaveFormat(AudioManager.INPUT_SAMPLE_RATE, 2)); + JitterBuffers[audio.TransmissionGuid] = jitterBuffer; + MixingSampleProvider.AddMixerInput(new Pcm16BitToSampleProvider(jitterBuffer)); + } + + jitterBuffer.AddSamples(new JitterBufferAudio { Audio = SeperateAudio(ConversionHelpers.ShortArrayToByteArray(audio.PcmAudioShort), audio.ReceivedRadio), - PacketNumber = audio.PacketNumber + PacketNumber = audio.PacketNumber, + TransmissionGuid = audio.TransmissionGuid }); //timer.Stop(); @@ -313,7 +322,8 @@ private short RandomShort() //destructor to clear up opus ~ClientAudioProvider() { - _decoder.Dispose(); + MixingSampleProvider?.RemoveAllMixerInputs(); + _decoder?.Dispose(); _decoder = null; } diff --git a/DCS-SR-Client/Network/DCS/DCSRadioSyncHandler.cs b/DCS-SR-Client/Network/DCS/DCSRadioSyncHandler.cs index ecb9d3163..e57eac6fe 100644 --- a/DCS-SR-Client/Network/DCS/DCSRadioSyncHandler.cs +++ b/DCS-SR-Client/Network/DCS/DCSRadioSyncHandler.cs @@ -301,6 +301,7 @@ private bool UpdateRadio(DCSPlayerRadioInfo message) clientRadio.freqMin = 1; clientRadio.freqMax = 1; clientRadio.secFreq = 0; + clientRadio.retransmit = false; clientRadio.modulation = RadioInformation.Modulation.DISABLED; clientRadio.name = "No Radio"; @@ -323,6 +324,7 @@ private bool UpdateRadio(DCSPlayerRadioInfo message) clientRadio.freqMin = 1; clientRadio.freqMax = 1; clientRadio.secFreq = 0; + clientRadio.retransmit = false; clientRadio.modulation = RadioInformation.Modulation.DISABLED; clientRadio.name = "No Radio"; diff --git a/DCS-SR-Client/Network/UDPVoiceHandler.cs b/DCS-SR-Client/Network/UDPVoiceHandler.cs index 713ab4584..cee0b3b35 100644 --- a/DCS-SR-Client/Network/UDPVoiceHandler.cs +++ b/DCS-SR-Client/Network/UDPVoiceHandler.cs @@ -55,6 +55,7 @@ internal class UdpVoiceHandler private UdpClient _listener; private ulong _packetNumber = 1; + private ulong _retransmitPacketNumber = 1; private volatile bool _ptt; @@ -71,6 +72,9 @@ internal class UdpVoiceHandler private RadioReceivingState[] _radioReceivingState; + private byte[] _retransmitGuidBytes; + private string _retransmitGuid; + public UdpVoiceHandler(string guid, IPAddress address, int port, OpusDecoder decoder, AudioManager audioManager, InputDeviceManager inputManager) { @@ -79,6 +83,8 @@ public UdpVoiceHandler(string guid, IPAddress address, int port, OpusDecoder dec // _decoder = decoder; _audioManager = audioManager; _guidAsciiBytes = Encoding.ASCII.GetBytes(guid); + _retransmitGuid = ShortGuid.NewGuid().ToString(); + _retransmitGuidBytes = Encoding.ASCII.GetBytes(_retransmitGuid); _guid = guid; _address = address; @@ -212,6 +218,7 @@ public void Listen() StartPing(); _packetNumber = 1; //reset packet number + _retransmitPacketNumber = 1; while (!_stop) { @@ -405,6 +412,8 @@ private void UdpAudioDecode() if (radioReceivingPriorities.Count > 0) { + + //ALL GOOD! //create marker for bytes for (int i = 0; i < radioReceivingPriorities.Count; i++) @@ -433,7 +442,8 @@ private void UdpAudioDecode() LineOfSightLoss = destinationRadio .LineOfSightLoss, // Loss of 1.0 or greater is total loss - PacketNumber = udpVoicePacket.PacketNumber + PacketNumber = udpVoicePacket.PacketNumber, + TransmissionGuid = udpVoicePacket.TransmissionGuid }; @@ -446,7 +456,7 @@ private void UdpAudioDecode() { var audioDecryptable = audio.Decryptable || (audio.Encryption == 0); - //mark that we have decrpyted encrypted audio for sound effects + //mark that we have decrypted encrypted audio for sound effects if (audioDecryptable && (audio.Encryption > 0)) { _audioManager.PlaySoundEffectStartReceive(audio.ReceivedRadio, @@ -468,8 +478,8 @@ private void UdpAudioDecode() { transmitterName = transmittingClient.Name; - } - + } + var newRadioReceivingState = new RadioReceivingState { IsSecondary = destinationRadio.ReceivingState.IsSecondary, @@ -488,6 +498,9 @@ private void UdpAudioDecode() _audioManager.AddClientAudio(audio); } } + + //handle retransmission + RetransmitAudio(udpVoicePacket, radioReceivingPriorities); } } } @@ -508,6 +521,97 @@ private void UdpAudioDecode() } } + private void RetransmitAudio(UDPVoicePacket udpVoicePacket, List radioReceivingPriorities) + { + + if (udpVoicePacket.Guid == _guid) + { + return; + //my own transmission - throw away + } + + //Hop count can limit the retransmission too + var nodeLimit = _serverSettings.RetransmitNodeLimit; + + if (nodeLimit < udpVoicePacket.RetransmissionCount+1) + { + //Reached hop limit - no retransmit + return; + } + + //TODO check if it needs to be retransmitted + //if a radio needs to retransmit - dont play the Rx and Tx audio - as its just retransmiting the transmission? + //OR do it and also resend? + // store unique ID against the radio thats retransmitting from and use that for all the retransmisson messages and count the packet number up by radio + + // filter radios by ability to hear it AND decryption works + List retransmitOn = new List(); + //artificially limit some retransmissions - if encryption fails dont retransmit + + //from the subset of receiving radios - find any other radios that have retransmit - and dont retransmit on any with the same frequency? + //to stop loops? + + var receivingWithRetransmit = radioReceivingPriorities.Where(receivingRadio => (receivingRadio.Decryptable || (receivingRadio.Encryption == 0)) && receivingRadio.ReceivingRadio.retransmit); + + //radios able to retransmit + var radiosWithRetransmit = _clientStateSingleton.DcsPlayerRadioInfo.radios.Where(radio => radio.retransmit); + + //Check we're not retransmitting through a radio we just received on? + //TODO handle GUARD here - makes this logic more tricky! + foreach (var receivingRadio in receivingWithRetransmit) + { + radiosWithRetransmit = radiosWithRetransmit.Where(radio => !DCSPlayerRadioInfo.FreqCloseEnough(radio.freq, receivingRadio.Frequency)); + } + + var finalList = radiosWithRetransmit.ToList(); + + if (finalList.Count == 0) + { + //quit + return; + } + + //From the remaining list - build up a new outgoing packet + var frequencies = new double[finalList.Count]; + var encryptions = new byte[finalList.Count]; + var modulations = new byte[finalList.Count]; + + for (int i = 0; i < finalList.Count; i++) + { + frequencies[i] = finalList[i].freq; + encryptions[i] = finalList[i].encKey; + modulations[i] = (byte)finalList[i].modulation; + } + + //generate packet + var relayedPacket = new UDPVoicePacket + { + GuidBytes = _guidAsciiBytes, + AudioPart1Bytes = udpVoicePacket.AudioPart1Bytes, + AudioPart1Length = udpVoicePacket.AudioPart1Length, + Frequencies = frequencies, + UnitId = _clientStateSingleton.DcsPlayerRadioInfo.unitId, + Encryptions = encryptions, + Modulations = modulations, + PacketNumber = udpVoicePacket.RetransmissionCount == 0? _packetNumber++: udpVoicePacket.PacketNumber, //always want to keep packet number? //TODO check this + TransmissionBytes = udpVoicePacket.RetransmissionCount == 0? _retransmitGuidBytes:udpVoicePacket.TransmissionBytes, // TODO Check this? + RetransmissionCount = (byte)(udpVoicePacket.RetransmissionCount+1), + }; + + var packet = relayedPacket.EncodePacket(); + + try + { + _listener.Send(packet, packet.Length, + new IPEndPoint(_address, _port)); + } + catch (Exception) + { + } + + + } + private List CurrentlyBlockedRadios() { List transmitting = new List(); @@ -525,14 +629,14 @@ private List CurrentlyBlockedRadios() var currentRadio = _clientStateSingleton.DcsPlayerRadioInfo.radios[_clientStateSingleton.DcsPlayerRadioInfo.selected]; - if (currentRadio.modulation == RadioInformation.Modulation.FM - || currentRadio.modulation == RadioInformation.Modulation.AM - || currentRadio.modulation == RadioInformation.Modulation.MIDS - || currentRadio.modulation == RadioInformation.Modulation.HAVEQUICK) - { - //only AM and FM block - SATCOM etc dont - - transmitting.Add(_clientStateSingleton.DcsPlayerRadioInfo.selected); + if (currentRadio.modulation == RadioInformation.Modulation.FM + || currentRadio.modulation == RadioInformation.Modulation.AM + || currentRadio.modulation == RadioInformation.Modulation.MIDS + || currentRadio.modulation == RadioInformation.Modulation.HAVEQUICK) + { + //only AM and FM block - SATCOM etc dont + + transmitting.Add(_clientStateSingleton.DcsPlayerRadioInfo.selected); } diff --git a/DCS-SR-Client/Settings/SynchedServerSettings.cs b/DCS-SR-Client/Settings/SynchedServerSettings.cs index 9c65a59af..c4afd215a 100644 --- a/DCS-SR-Client/Settings/SynchedServerSettings.cs +++ b/DCS-SR-Client/Settings/SynchedServerSettings.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; +using System.Windows.Forms; using Ciribob.DCS.SimpleRadio.Standalone.Common.Setting; using NLog; @@ -17,6 +18,9 @@ public class SyncedServerSettings public List GlobalFrequencies { get; set; } = new List(); + // Node Limit of 0 means no retransmission + public int RetransmitNodeLimit { get; set; } = 0; + public SyncedServerSettings() { _settings = new ConcurrentDictionary(); @@ -72,6 +76,19 @@ public void Decode(Dictionary encoded) GlobalFrequencies = newList; } + else if(kvp.Key.Equals(ServerSettingsKeys.RETRANSMISSION_NODE_LIMIT.ToString())) + { + if (!int.TryParse(kvp.Value, out var nodeLimit)) + { + nodeLimit = 0; + } + else + { + RetransmitNodeLimit = nodeLimit; + } + + + } } } } diff --git a/DCS-SR-Client/UI/RadioOverlayWindow/RadioControlGroup.xaml b/DCS-SR-Client/UI/RadioOverlayWindow/RadioControlGroup.xaml index 3b1a7ef21..b82b10d9e 100644 --- a/DCS-SR-Client/UI/RadioOverlayWindow/RadioControlGroup.xaml +++ b/DCS-SR-Client/UI/RadioOverlayWindow/RadioControlGroup.xaml @@ -54,12 +54,13 @@ Padding="0" Text="Radio" /> - + -