i live in spain but the S is silent
All checks were successful
Build and push image / Build (push) Successful in 49s

This commit is contained in:
Jack
2023-09-28 00:53:27 +01:00
parent 2c62cd03fa
commit 4d94fbf54f
279 changed files with 5474 additions and 6863 deletions

View File

@@ -0,0 +1,142 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
namespace ksBroadcastingNetwork
{
public class ACCUdpRemoteClient : IDisposable
{
private UdpClient _client;
private Task _listenerTask;
public BroadcastingNetworkProtocol MessageHandler { get; }
public string IpPort { get; }
public string DisplayName { get; }
public string ConnectionPassword { get; }
public string CommandPassword { get; }
public int MsRealtimeUpdateInterval { get; }
/// <summary>
/// To get the events delivered inside the UI thread, just create this object from the UI thread/synchronization context.
/// </summary>
public ACCUdpRemoteClient(string ip, int port, string displayName, string connectionPassword, string commandPassword, int msRealtimeUpdateInterval)
{
IpPort = $"{ip}:{port}";
MessageHandler = new BroadcastingNetworkProtocol(IpPort, Send);
_client = new UdpClient();
_client.Connect(ip, port);
DisplayName = displayName;
ConnectionPassword = connectionPassword;
CommandPassword = commandPassword;
MsRealtimeUpdateInterval = msRealtimeUpdateInterval;
_listenerTask = ConnectAndRun();
}
private void Send(byte[] payload)
{
var sent = _client.Send(payload, payload.Length);
}
public void Shutdown()
{
ShutdownAsnyc().ContinueWith(t =>
{
if (t.Exception?.InnerExceptions?.Any() == true)
System.Diagnostics.Debug.WriteLine($"Client shut down with {t.Exception.InnerExceptions.Count} errors");
else
System.Diagnostics.Debug.WriteLine("Client shut down asynchronously");
});
}
public async Task ShutdownAsnyc()
{
if (_listenerTask != null && !_listenerTask.IsCompleted)
{
MessageHandler.Disconnect();
_client.Close();
_client = null;
await _listenerTask;
}
}
private async Task ConnectAndRun()
{
MessageHandler.RequestConnection(DisplayName, ConnectionPassword, MsRealtimeUpdateInterval, CommandPassword);
while (_client != null)
{
try
{
var udpPacket = await _client.ReceiveAsync();
using (var ms = new System.IO.MemoryStream(udpPacket.Buffer))
using (var reader = new System.IO.BinaryReader(ms))
{
MessageHandler.ProcessMessage(reader);
}
}
catch (ObjectDisposedException)
{
// Shutdown happened
break;
}
catch (Exception ex)
{
// Other exceptions
System.Diagnostics.Debug.WriteLine(ex);
}
}
}
#region IDisposable Support
private bool disposedValue = false; // To detect redundant calls
protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
{
if (disposing)
{
try
{
if (_client != null)
{
_client.Close();
_client.Dispose();
_client = null;
}
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine(ex);
}
}
// TODO: free unmanaged resources (unmanaged objects) and override a finalizer below.
// TODO: set large fields to null.
disposedValue = true;
}
}
// TODO: override a finalizer only if Dispose(bool disposing) above has code to free unmanaged resources.
// ~ACCUdpRemoteClient() {
// // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
// Dispose(false);
// }
// This code added to correctly implement the disposable pattern.
public void Dispose()
{
// Do not change this code. Put cleanup code in Dispose(bool disposing) above.
Dispose(true);
// TODO: uncomment the following line if the finalizer is overridden above.
// GC.SuppressFinalize(this);
}
#endregion
}
}

View File

@@ -0,0 +1,159 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ksBroadcastingNetwork
{
public enum DriverCategory
{
Platinum = 3,
Gold = 2,
Silver = 1,
Bronze = 0,
Error = 255
}
public enum LapType
{
ERROR = 0,
Outlap = 1,
Regular = 2,
Inlap = 3
}
public enum CarLocationEnum
{
NONE = 0,
Track = 1,
Pitlane = 2,
PitEntry = 3,
PitExit = 4
}
public enum SessionPhase
{
NONE = 0,
Starting = 1,
PreFormation = 2,
FormationLap = 3,
PreSession = 4,
Session = 5,
SessionOver = 6,
PostSession = 7,
ResultUI = 8
};
public enum RaceSessionType
{
Practice = 0,
Qualifying = 4,
Superpole = 9,
Race = 10,
Hotlap = 11,
Hotstint = 12,
HotlapSuperpole = 13,
Replay = 14
};
public enum BroadcastingCarEventType
{
None = 0,
GreenFlag = 1,
SessionOver = 2,
PenaltyCommMsg = 3,
Accident = 4,
LapCompleted = 5,
BestSessionLap = 6,
BestPersonalLap = 7
};
public enum NationalityEnum
{
Any = 0,
Italy = 1,
Germany = 2,
France = 3,
Spain = 4,
GreatBritain = 5,
Hungary = 6,
Belgium = 7,
Switzerland = 8,
Austria = 9,
Russia = 10,
Thailand = 11,
Netherlands = 12,
Poland = 13,
Argentina = 14,
Monaco = 15,
Ireland = 16,
Brazil = 17,
SouthAfrica = 18,
PuertoRico = 19,
Slovakia = 20,
Oman = 21,
Greece = 22,
SaudiArabia = 23,
Norway = 24,
Turkey = 25,
SouthKorea = 26,
Lebanon = 27,
Armenia = 28,
Mexico = 29,
Sweden = 30,
Finland = 31,
Denmark = 32,
Croatia = 33,
Canada = 34,
China = 35,
Portugal = 36,
Singapore = 37,
Indonesia = 38,
USA = 39,
NewZealand = 40,
Australia = 41,
SanMarino = 42,
UAE = 43,
Luxembourg = 44,
Kuwait = 45,
HongKong = 46,
Colombia = 47,
Japan = 48,
Andorra = 49,
Azerbaijan = 50,
Bulgaria = 51,
Cuba = 52,
CzechRepublic = 53,
Estonia = 54,
Georgia = 55,
India = 56,
Israel = 57,
Jamaica = 58,
Latvia = 59,
Lithuania = 60,
Macau = 61,
Malaysia = 62,
Nepal = 63,
NewCaledonia = 64,
Nigeria = 65,
NorthernIreland = 66,
PapuaNewGuinea = 67,
Philippines = 68,
Qatar = 69,
Romania = 70,
Scotland = 71,
Serbia = 72,
Slovenia = 73,
Taiwan = 74,
Ukraine = 75,
Venezuela = 76,
Wales = 77,
Iran = 78,
Bahrain = 79,
Zimbabwe = 80,
ChineseTaipei = 81,
Chile = 82,
Uruguay = 83,
Madagascar = 84
};
}

View File

@@ -0,0 +1,516 @@
using ksBroadcastingNetwork.Structs;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ksBroadcastingNetwork
{
public enum OutboundMessageTypes : byte
{
REGISTER_COMMAND_APPLICATION = 1,
UNREGISTER_COMMAND_APPLICATION = 9,
REQUEST_ENTRY_LIST = 10,
REQUEST_TRACK_DATA = 11,
CHANGE_HUD_PAGE = 49,
CHANGE_FOCUS = 50,
INSTANT_REPLAY_REQUEST = 51,
PLAY_MANUAL_REPLAY_HIGHLIGHT = 52, // TODO, but planned
SAVE_MANUAL_REPLAY_HIGHLIGHT = 60 // TODO, but planned: saving manual replays gives distributed clients the possibility to see the play the same replay
}
public enum InboundMessageTypes : byte
{
REGISTRATION_RESULT = 1,
REALTIME_UPDATE = 2,
REALTIME_CAR_UPDATE = 3,
ENTRY_LIST = 4,
ENTRY_LIST_CAR = 6,
TRACK_DATA = 5,
BROADCASTING_EVENT = 7
}
public class BroadcastingNetworkProtocol
{
public const int BROADCASTING_PROTOCOL_VERSION = 4;
private string ConnectionIdentifier { get; }
private SendMessageDelegate Send { get; }
public int ConnectionId { get; private set; }
public float TrackMeters { get; private set; }
internal delegate void SendMessageDelegate(byte[] payload);
#region Events
public delegate void ConnectionStateChangedDelegate(int connectionId, bool connectionSuccess, bool isReadonly, string error);
public event ConnectionStateChangedDelegate OnConnectionStateChanged;
public delegate void TrackDataUpdateDelegate(string sender, TrackData trackUpdate);
public event TrackDataUpdateDelegate OnTrackDataUpdate;
public delegate void EntryListUpdateDelegate(string sender, CarInfo car);
public event EntryListUpdateDelegate OnEntrylistUpdate;
public delegate void RealtimeUpdateDelegate(string sender, RealtimeUpdate update);
public event RealtimeUpdateDelegate OnRealtimeUpdate;
public delegate void RealtimeCarUpdateDelegate(string sender, RealtimeCarUpdate carUpdate);
public event RealtimeCarUpdateDelegate OnRealtimeCarUpdate;
public delegate void BroadcastingEventDelegate(string sender, BroadcastingEvent evt);
public event BroadcastingEventDelegate OnBroadcastingEvent;
#endregion
#region EntryList handling
// To avoid huge UDP pakets for longer entry lists, we will first receive the indexes of cars and drivers,
// cache the entries and wait for the detailled updates
List<CarInfo> _entryListCars = new List<CarInfo>();
#endregion
#region optional failsafety - detect when we have a desync and need a new entry list
DateTime lastEntrylistRequest = DateTime.Now;
#endregion
internal BroadcastingNetworkProtocol(string connectionIdentifier, SendMessageDelegate sendMessageDelegate)
{
if (string.IsNullOrEmpty(connectionIdentifier))
throw new ArgumentNullException(nameof(connectionIdentifier), $"No connection identifier set; we use this to distinguish different connections. Using the remote IP:Port is a good idea");
if (sendMessageDelegate == null)
throw new ArgumentNullException(nameof(sendMessageDelegate), $"The protocol class doesn't know anything about the network layer; please put a callback we can use to send data via UDP");
ConnectionIdentifier = connectionIdentifier;
Send = sendMessageDelegate;
}
internal void ProcessMessage(BinaryReader br)
{
// Any message starts with an 1-byte command type
var messageType = (InboundMessageTypes)br.ReadByte();
switch (messageType)
{
case InboundMessageTypes.REGISTRATION_RESULT:
{
ConnectionId = br.ReadInt32();
var connectionSuccess = br.ReadByte() > 0;
var isReadonly = br.ReadByte() == 0;
var errMsg = ReadString(br);
OnConnectionStateChanged?.Invoke(ConnectionId, connectionSuccess, isReadonly, errMsg);
// In case this was successful, we will request the initial data
RequestEntryList();
RequestTrackData();
}
break;
case InboundMessageTypes.ENTRY_LIST:
{
_entryListCars.Clear();
var connectionId = br.ReadInt32();
var carEntryCount = br.ReadUInt16();
for (int i = 0; i < carEntryCount; i++)
{
_entryListCars.Add(new CarInfo(br.ReadUInt16()));
}
}
break;
case InboundMessageTypes.ENTRY_LIST_CAR:
{
var carId = br.ReadUInt16();
var carInfo = _entryListCars.SingleOrDefault(x => x.CarIndex == carId);
if(carInfo == null)
{
System.Diagnostics.Debug.WriteLine($"Entry list update for unknown carIndex {carId}");
break;
}
carInfo.CarModelType = br.ReadByte(); // Byte sized car model
carInfo.TeamName = ReadString(br);
carInfo.RaceNumber = br.ReadInt32();
carInfo.CupCategory = br.ReadByte(); // Cup: Overall/Pro = 0, ProAm = 1, Am = 2, Silver = 3, National = 4
carInfo.CurrentDriverIndex = br.ReadByte();
carInfo.Nationality = (NationalityEnum)br.ReadUInt16();
// Now the drivers on this car:
var driversOnCarCount = br.ReadByte();
for (int di = 0; di < driversOnCarCount; di++)
{
var driverInfo = new DriverInfo();
driverInfo.FirstName = ReadString(br);
driverInfo.LastName = ReadString(br);
driverInfo.ShortName = ReadString(br);
driverInfo.Category = (DriverCategory)br.ReadByte(); // Platinum = 3, Gold = 2, Silver = 1, Bronze = 0
// new in 1.13.11:
driverInfo.Nationality = (NationalityEnum)br.ReadUInt16();
carInfo.AddDriver(driverInfo);
}
OnEntrylistUpdate?.Invoke(ConnectionIdentifier, carInfo);
}
break;
case InboundMessageTypes.REALTIME_UPDATE:
{
RealtimeUpdate update = new RealtimeUpdate();
update.EventIndex = (int)br.ReadUInt16();
update.SessionIndex = (int)br.ReadUInt16();
update.SessionType = (RaceSessionType)br.ReadByte();
update.Phase = (SessionPhase)br.ReadByte();
var sessionTime = br.ReadSingle();
update.SessionTime = TimeSpan.FromMilliseconds(sessionTime);
var sessionEndTime = br.ReadSingle();
update.SessionEndTime = TimeSpan.FromMilliseconds(sessionEndTime);
update.FocusedCarIndex = br.ReadInt32();
update.ActiveCameraSet = ReadString(br);
update.ActiveCamera = ReadString(br);
update.CurrentHudPage = ReadString(br);
update.IsReplayPlaying = br.ReadByte() > 0;
if (update.IsReplayPlaying)
{
update.ReplaySessionTime = br.ReadSingle();
update.ReplayRemainingTime = br.ReadSingle();
}
update.TimeOfDay = TimeSpan.FromMilliseconds(br.ReadSingle());
update.AmbientTemp = br.ReadByte();
update.TrackTemp = br.ReadByte();
update.Clouds = br.ReadByte() / 10.0f;
update.RainLevel = br.ReadByte() / 10.0f;
update.Wetness = br.ReadByte() / 10.0f;
update.BestSessionLap = ReadLap(br);
OnRealtimeUpdate?.Invoke(ConnectionIdentifier, update);
}
break;
case InboundMessageTypes.REALTIME_CAR_UPDATE:
{
RealtimeCarUpdate carUpdate = new RealtimeCarUpdate();
carUpdate.CarIndex = br.ReadUInt16();
carUpdate.DriverIndex = br.ReadUInt16(); // Driver swap will make this change
carUpdate.DriverCount = br.ReadByte();
carUpdate.Gear = br.ReadByte() - 2; // -2 makes the R -1, N 0 and the rest as-is
carUpdate.WorldPosX = br.ReadSingle();
carUpdate.WorldPosY = br.ReadSingle();
carUpdate.Yaw = br.ReadSingle();
carUpdate.CarLocation = (CarLocationEnum)br.ReadByte(); // - , Track, Pitlane, PitEntry, PitExit = 4
carUpdate.Kmh = br.ReadUInt16();
carUpdate.Position = br.ReadUInt16(); // official P/Q/R position (1 based)
carUpdate.CupPosition = br.ReadUInt16(); // official P/Q/R position (1 based)
carUpdate.TrackPosition = br.ReadUInt16(); // position on track (1 based)
carUpdate.SplinePosition = br.ReadSingle(); // track position between 0.0 and 1.0
carUpdate.Laps = br.ReadUInt16();
carUpdate.Delta = br.ReadInt32(); // Realtime delta to best session lap
carUpdate.BestSessionLap = ReadLap(br);
carUpdate.LastLap = ReadLap(br);
carUpdate.CurrentLap = ReadLap(br);
// the concept is: "don't know a car or driver? ask for an entry list update"
var carEntry = _entryListCars.FirstOrDefault(x => x.CarIndex == carUpdate.CarIndex);
if(carEntry == null || carEntry.Drivers.Count != carUpdate.DriverCount)
{
if ((DateTime.Now - lastEntrylistRequest).TotalSeconds > 1)
{
lastEntrylistRequest = DateTime.Now;
RequestEntryList();
System.Diagnostics.Debug.WriteLine($"CarUpdate {carUpdate.CarIndex}|{carUpdate.DriverIndex} not know, will ask for new EntryList");
}
}
else
{
OnRealtimeCarUpdate?.Invoke(ConnectionIdentifier, carUpdate);
}
}
break;
case InboundMessageTypes.TRACK_DATA:
{
var connectionId = br.ReadInt32();
var trackData = new TrackData();
trackData.TrackName = ReadString(br);
trackData.TrackId = br.ReadInt32();
trackData.TrackMeters = br.ReadInt32();
TrackMeters = trackData.TrackMeters > 0 ? trackData.TrackMeters : -1;
trackData.CameraSets = new Dictionary<string, List<string>>();
var cameraSetCount = br.ReadByte();
for (int camSet = 0; camSet < cameraSetCount; camSet++)
{
var camSetName = ReadString(br);
trackData.CameraSets.Add(camSetName, new List<string>());
var cameraCount = br.ReadByte();
for (int cam = 0; cam < cameraCount; cam++)
{
var cameraName = ReadString(br);
trackData.CameraSets[camSetName].Add(cameraName);
}
}
var hudPages = new List<string>();
var hudPagesCount = br.ReadByte();
for (int i = 0; i < hudPagesCount; i++)
{
hudPages.Add(ReadString(br));
}
trackData.HUDPages = hudPages;
OnTrackDataUpdate?.Invoke(ConnectionIdentifier, trackData);
}
break;
case InboundMessageTypes.BROADCASTING_EVENT:
{
BroadcastingEvent evt = new BroadcastingEvent()
{
Type = (BroadcastingCarEventType)br.ReadByte(),
Msg = ReadString(br),
TimeMs = br.ReadInt32(),
CarId = br.ReadInt32(),
};
evt.CarData = _entryListCars.FirstOrDefault(x => x.CarIndex == evt.CarId);
OnBroadcastingEvent?.Invoke(ConnectionIdentifier, evt);
}
break;
default:
break;
}
}
/// <summary>
/// Laps are always sent in a common way, it makes sense to have a shared function to parse them
/// </summary>
private static LapInfo ReadLap(BinaryReader br)
{
var lap = new LapInfo();
lap.LaptimeMS = br.ReadInt32();
lap.CarIndex = br.ReadUInt16();
lap.DriverIndex = br.ReadUInt16();
var splitCount = br.ReadByte();
for (int i = 0; i < splitCount; i++)
lap.Splits.Add(br.ReadInt32());
lap.IsInvalid = br.ReadByte() > 0;
lap.IsValidForBest = br.ReadByte() > 0;
var isOutlap = br.ReadByte() > 0;
var isInlap = br.ReadByte() > 0;
if (isOutlap)
lap.Type = LapType.Outlap;
else if (isInlap)
lap.Type = LapType.Inlap;
else
lap.Type = LapType.Regular;
// Now it's possible that this is "no" lap that doesn't even include a
// first split, we can detect this by comparing with int32.Max
while (lap.Splits.Count < 3)
{
lap.Splits.Add(null);
}
// "null" entries are Int32.Max, in the C# world we can replace this to null
for (int i = 0; i < lap.Splits.Count; i++)
if (lap.Splits[i] == Int32.MaxValue)
lap.Splits[i] = null;
if (lap.LaptimeMS == Int32.MaxValue)
lap.LaptimeMS = null;
return lap;
}
private static string ReadString(BinaryReader br)
{
var length = br.ReadUInt16();
var bytes = br.ReadBytes(length);
return Encoding.UTF8.GetString(bytes);
}
private static void WriteString(BinaryWriter bw, string s)
{
var bytes = Encoding.UTF8.GetBytes(s);
bw.Write(Convert.ToUInt16(bytes.Length));
bw.Write(bytes);
}
/// <summary>
/// Will try to register this client in the targeted ACC instance.
/// Needs to be called once, before anything else can happen.
/// </summary>
/// <param name="connectionPassword"></param>
/// <param name="msRealtimeUpdateInterval"></param>
/// <param name="commandPassword"></param>
internal void RequestConnection(string displayName, string connectionPassword, int msRealtimeUpdateInterval, string commandPassword)
{
using (var ms = new MemoryStream())
using (var br = new BinaryWriter(ms))
{
br.Write((byte)OutboundMessageTypes.REGISTER_COMMAND_APPLICATION); // First byte is always the command type
br.Write((byte)BROADCASTING_PROTOCOL_VERSION);
WriteString(br, displayName);
WriteString(br, connectionPassword);
br.Write(msRealtimeUpdateInterval);
WriteString(br, commandPassword);
Send(ms.ToArray());
}
}
internal void Disconnect()
{
using (var ms = new MemoryStream())
using (var br = new BinaryWriter(ms))
{
br.Write((byte)OutboundMessageTypes.UNREGISTER_COMMAND_APPLICATION); // First byte is always the command type
Send(ms.ToArray());
}
}
/// <summary>
/// Will ask the ACC client for an updated entry list, containing all car and driver data.
/// The client will send this automatically when something changes; however if you detect a carIndex or driverIndex, this may cure the
/// problem for future updates
/// </summary>
private void RequestEntryList()
{
using (var ms = new MemoryStream())
using (var br = new BinaryWriter(ms))
{
br.Write((byte)OutboundMessageTypes.REQUEST_ENTRY_LIST); // First byte is always the command type
br.Write((int)ConnectionId);
Send(ms.ToArray());
}
}
private void RequestTrackData()
{
using (var ms = new MemoryStream())
using (var br = new BinaryWriter(ms))
{
br.Write((byte)OutboundMessageTypes.REQUEST_TRACK_DATA); // First byte is always the command type
br.Write((int)ConnectionId);
Send(ms.ToArray());
}
}
public void SetFocus(UInt16 carIndex)
{
SetFocusInternal(carIndex, null, null);
}
/// <summary>
/// Always put both cam + cam set; even if it doesn't make sense
/// </summary>
public void SetCamera(string cameraSet, string camera)
{
SetFocusInternal(null, cameraSet, camera);
}
public void SetFocus(UInt16 carIndex, string cameraSet, string camera)
{
SetFocusInternal(carIndex, cameraSet, camera);
}
/// <summary>
/// Sends the request to change the focused car and/or the camera used.
/// The idea is that this often wants to be triggered together, so this is a all-in-one function.
/// This way we can make sure the switch happens in the same frame, even in more complex scenarios
/// </summary>
private void SetFocusInternal(UInt16? carIndex, string cameraSet, string camera)
{
using (var ms = new MemoryStream())
using (var bw = new BinaryWriter(ms))
{
bw.Write((byte)OutboundMessageTypes.CHANGE_FOCUS); // First byte is always the command type
bw.Write((int)ConnectionId);
if (!carIndex.HasValue)
{
bw.Write((byte)0); // No change of focused car
}
else
{
bw.Write((byte)1);
bw.Write((UInt16)(carIndex.Value));
}
if (string.IsNullOrEmpty(cameraSet) || string.IsNullOrEmpty(camera))
{
bw.Write((byte)0); // No change of camera set or camera
}
else
{
bw.Write((byte)1);
WriteString(bw, cameraSet);
WriteString(bw, camera);
}
Send(ms.ToArray());
}
}
public void RequestInstantReplay(float startSessionTime, float durationMS, int initialFocusedCarIndex = -1, string initialCameraSet = "", string initialCamera = "")
{
using (var ms = new MemoryStream())
using (var bw = new BinaryWriter(ms))
{
bw.Write((byte)OutboundMessageTypes.INSTANT_REPLAY_REQUEST); // First byte is always the command type
bw.Write((int)ConnectionId);
bw.Write((float)startSessionTime);
bw.Write((float)durationMS);
bw.Write((int)initialFocusedCarIndex);
WriteString(bw, initialCameraSet);
WriteString(bw, initialCamera);
Send(ms.ToArray());
}
}
public void RequestHUDPage(string hudPage)
{
using (var ms = new MemoryStream())
using (var bw = new BinaryWriter(ms))
{
bw.Write((byte)OutboundMessageTypes.CHANGE_HUD_PAGE); // First byte is always the command type
bw.Write((int)ConnectionId);
WriteString(bw, hudPage);
Send(ms.ToArray());
}
}
}
}

View File

@@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("ksBroadcastingNetwork")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("ksBroadcastingNetwork")]
[assembly: AssemblyCopyright("Copyright © 2018")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("1ef9a746-3771-4052-b61b-04bdb3dc381a")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ksBroadcastingNetwork.Structs
{
public struct BroadcastingEvent
{
public BroadcastingCarEventType Type { get; internal set; }
public string Msg { get; internal set; }
public int TimeMs { get; internal set; }
public int CarId { get; internal set; }
public CarInfo CarData { get; internal set; }
}
}

View File

@@ -0,0 +1,37 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ksBroadcastingNetwork.Structs
{
public class CarInfo
{
public ushort CarIndex { get; }
public byte CarModelType { get; internal set; }
public string TeamName { get; internal set; }
public int RaceNumber { get; internal set; }
public byte CupCategory { get; internal set; }
public int CurrentDriverIndex { get; internal set; }
public IList<DriverInfo> Drivers { get; } = new List<DriverInfo>();
public NationalityEnum Nationality { get; internal set; }
public CarInfo(ushort carIndex)
{
CarIndex = carIndex;
}
internal void AddDriver(DriverInfo driverInfo)
{
Drivers.Add(driverInfo);
}
public string GetCurrentDriverName()
{
if (CurrentDriverIndex < Drivers.Count)
return Drivers[CurrentDriverIndex].LastName;
return "nobody(?)";
}
}
}

View File

@@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ksBroadcastingNetwork.Structs
{
public struct DriverInfo
{
public string FirstName { get; internal set; }
public string LastName { get; internal set; }
public string ShortName { get; internal set; }
public DriverCategory Category { get; internal set; }
public NationalityEnum Nationality { get; internal set; }
}
}

View File

@@ -0,0 +1,24 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ksBroadcastingNetwork.Structs
{
public class LapInfo
{
public int? LaptimeMS { get; internal set; }
public List<int?> Splits { get; } = new List<int?>();
public ushort CarIndex { get; internal set; }
public ushort DriverIndex { get; internal set; }
public bool IsInvalid { get; internal set; }
public bool IsValidForBest { get; internal set; }
public LapType Type { get; internal set; }
public override string ToString()
{
return $"{LaptimeMS, 5}|{string.Join("|", Splits)}";
}
}
}

View File

@@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ksBroadcastingNetwork.Structs
{
public struct RealtimeCarUpdate
{
public int CarIndex { get; internal set; }
public int DriverIndex { get; internal set; }
public int Gear { get; internal set; }
public float WorldPosX { get; internal set; }
public float WorldPosY { get; internal set; }
public float Yaw { get; internal set; }
public CarLocationEnum CarLocation { get; internal set; }
public int Kmh { get; internal set; }
public int Position { get; internal set; }
public int TrackPosition { get; internal set; }
public float SplinePosition { get; internal set; }
public int Delta { get; internal set; }
public LapInfo BestSessionLap { get; internal set; }
public LapInfo LastLap { get; internal set; }
public LapInfo CurrentLap { get; internal set; }
public int Laps { get; internal set; }
public ushort CupPosition { get; internal set; }
public byte DriverCount { get; internal set; }
}
}

View File

@@ -0,0 +1,36 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ksBroadcastingNetwork.Structs
{
public struct RealtimeUpdate
{
public int EventIndex { get; internal set; }
public int SessionIndex { get; internal set; }
public SessionPhase Phase { get; internal set; }
public TimeSpan SessionTime { get; internal set; }
public TimeSpan RemainingTime { get; internal set; }
public TimeSpan TimeOfDay { get; internal set; }
public float RainLevel { get; internal set; }
public float Clouds { get; internal set; }
public float Wetness { get; internal set; }
public LapInfo BestSessionLap { get; internal set; }
public ushort BestLapCarIndex { get; internal set; }
public ushort BestLapDriverIndex { get; internal set; }
public int FocusedCarIndex { get; internal set; }
public string ActiveCameraSet { get; internal set; }
public string ActiveCamera { get; internal set; }
public bool IsReplayPlaying { get; internal set; }
public float ReplaySessionTime { get; internal set; }
public float ReplayRemainingTime { get; internal set; }
public TimeSpan SessionRemainingTime { get; internal set; }
public TimeSpan SessionEndTime { get; internal set; }
public RaceSessionType SessionType { get; internal set; }
public byte AmbientTemp { get; internal set; }
public byte TrackTemp { get; internal set; }
public string CurrentHudPage { get; internal set; }
}
}

View File

@@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ksBroadcastingNetwork.Structs
{
public struct TrackData
{
public string TrackName { get; internal set; }
public int TrackId { get; internal set; }
public float TrackMeters { get; internal set; }
public Dictionary<string, List<string>> CameraSets { get; internal set; }
public IEnumerable<string> HUDPages { get; internal set; }
}
}

View File

@@ -0,0 +1,57 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{1EF9A746-3771-4052-B61B-04BDB3DC381A}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>ksBroadcastingNetwork</RootNamespace>
<AssemblyName>ksBroadcastingNetwork</AssemblyName>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<Deterministic>true</Deterministic>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="ACCUdpRemoteClient.cs" />
<Compile Include="BroadcastingEnums.cs" />
<Compile Include="BroadcastingNetworkProtocol.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Structs\BroadcastingEvent.cs" />
<Compile Include="Structs\CarInfo.cs" />
<Compile Include="Structs\DriverInfo.cs" />
<Compile Include="Structs\LapInfo.cs" />
<Compile Include="Structs\RealtimeCarUpdate.cs" />
<Compile Include="Structs\RealtimeUpdate.cs" />
<Compile Include="Structs\TrackData.cs" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View File

@@ -0,0 +1,102 @@
Server Log Start
Server Version 212
SessionStateChange: 1 start: 40.000000 end: 5000.000000
Sent Lobby Registration Request with trackName hungaroring
RegisterToLobby succeeded
WARNING: Lobby accepted connection
Sent config to kson
Sent connected drivers list to kson
SessionStateChange: 4 start: 5002.000000 end: 5002.000000
Sent new session state to kson
SessionStateChange: 5 start: 5008.000000 end: 1805008.000000
Sent new session state to kson
SessionStateChange: 6 start: 5008.000000 end: 1820009.000000
Sent new session state to kson
SessionStateChange: 7 start: 5008.000000 end: 1830012.000000
Sent new session state to kson
SessionStateChange: 8 start: 5008.000000 end: 1845015.000000
Sent new session state to kson
SessionOverUpdate currentSessionIndex: 1 currentEventIndex: 0
SessionStateChange: 1 start: 1845063.000000 end: 5000.000000
Sent new session state to kson
SessionStateChange: 4 start: 5003.000000 end: 5003.000000
Sent new session state to kson
SessionStateChange: 5 start: 5010.000000 end: 3605010.000000
Sent new session state to kson
New connection received 336
TCP PACKET received with ID:9
ACP_REQUEST_CONNECTION
CLIENT VERSION: 212
addNewConnectedCar: new car connected, adding it to the list of connected cars
Found and added a new player
New Connection created, new connectedCarId: 1 new connectionId: 1
Sent connected drivers list to kson
SessionStateChange: 1 start: 2473227.000000 end: 5000.000000
Sent new session state to kson
UDP Packet id: 19
TCP PACKET received with ID:44
TCP PACKET received with ID:44
TCP PACKET received with ID:41
TCP PACKET received with ID:45
TCP PACKET received with ID:48
SessionStateChange: 4 start: 5005.000000 end: 5005.000000
Sent new session state to kson
SessionStateChange: 5 start: 5012.000000 end: 1805012.000000
Sent new session state to kson
TCP PACKET received with ID:50
ACP_CAR_LOCATION_UPDATE location updated for carIndex 0
TCP PACKET received with ID:50
ACP_CAR_LOCATION_UPDATE location updated for carIndex 0
TCP PACKET received with ID:49
ACP_RACE_MANAGER_INFO_COMPLETED connectionId 1 is ready to reaceive packets
TCP PACKET received with ID:50
ACP_CAR_LOCATION_UPDATE location updated for carIndex 0
TCP PACKET received with ID:50
ACP_CAR_LOCATION_UPDATE location updated for carIndex 0
TCP PACKET received with ID:50
ACP_CAR_LOCATION_UPDATE location updated for carIndex 0
New connection received 344
TCP PACKET received with ID:9
ACP_REQUEST_CONNECTION
CLIENT VERSION: 212
addNewConnectedCar: new car connected, adding it to the list of connected cars
Found and added a new player
New Connection created, new connectedCarId: 2 new connectionId: 2
Sent connected drivers list to kson
UDP Packet id: 19
TCP PACKET received with ID:44
TCP PACKET received with ID:44
TCP PACKET received with ID:41
TCP PACKET received with ID:45
TCP PACKET received with ID:48
TCP PACKET received with ID:49
ACP_RACE_MANAGER_INFO_COMPLETED connectionId 2 is ready to reaceive packets
TCP PACKET received with ID:34
CAR_INFO_REQUEST for ID: 2
Car Info Response sent idRequested: 2, carIndex: 1, driverIndex: 1
TCP PACKET received with ID:50
ACP_CAR_LOCATION_UPDATE location updated for carIndex 1
TCP PACKET received with ID:50
ACP_CAR_LOCATION_UPDATE location updated for carIndex 1
TCP PACKET received with ID:34
CAR_INFO_REQUEST for ID: 1
Car Info Response sent idRequested: 1, carIndex: 0, driverIndex: 0
TCP PACKET received with ID:50
ACP_CAR_LOCATION_UPDATE location updated for carIndex 1
TCP PACKET received with ID:33
TCP PACKET received with ID:50
ACP_CAR_LOCATION_UPDATE location updated for carIndex 1
TCP PACKET received with ID:50
ACP_CAR_LOCATION_UPDATE location updated for carIndex 1
TCP PACKET received with ID:33
TCP PACKET received with ID:50
ACP_CAR_LOCATION_UPDATE location updated for carIndex 1
TCP PACKET received with ID:50
ACP_CAR_LOCATION_UPDATE location updated for carIndex 0
TCP PACKET received with ID:50
ACP_CAR_LOCATION_UPDATE location updated for carIndex 0
TCP PACKET received with ID:50
ACP_CAR_LOCATION_UPDATE location updated for carIndex 0
TCP PACKET received with ID:33
TCP PACKET received with ID:50
ACP_CAR_LOCATION_UPDATE