protected override void OnNewSession(MsgSessionInfo msg) { PluginManager.EnableRealtimeReport(100); PluginManager.Log("==============================="); PluginManager.Log("==============================="); PluginManager.Log("OnNewSession: " + msg.Name + "@" + msg.ServerName); PluginManager.Log("==============================="); PluginManager.Log("==============================="); var server_config_ini = GatherServerConfigIni(); var trackId = msg.Track + "[" + msg.TrackConfig + "]"; MRBackend.NewSessionWithConfigAsync(msg.ServerName, trackId , msg.SessionType, msg.Laps, msg.WaitTime, msg.SessionDuration, msg.AmbientTemp, msg.RoadTemp, msg.ElapsedMS , TrustToken, _fingerprint, PluginVersion, -1, -1, -1, server_config_ini); var maxDrivers = TryParseMaxDrivers(server_config_ini); PluginManager.Log($"Max drivers: {maxDrivers}"); for (byte i = 0; i < maxDrivers; i++) { PluginManager.RequestCarInfo(i); } CurrentSessionStartTime = msg.CreationDate.AddMilliseconds(msg.ElapsedMS * -1); _distancesToReport.Clear(); if (trackId != CurrentTrackDefinition?.TrackName) { ReloadTrackDefinition(trackId); } }
protected override void OnCollision(MsgClientEvent msg) { // Contact handling is now done by the backend completely - that is we just report any // collision with another car if (msg.Subtype == (byte)ACSProtocol.MessageType.ACSP_CE_COLLISION_WITH_CAR) { DriverInfo driver = null; if (!PluginManager.TryGetDriverInfo(Convert.ToByte(msg.CarId), out driver)) { throw new Exception("Driver not found: " + msg.CarId); } DriverInfo otherDriver = null; if (!PluginManager.TryGetDriverInfo(Convert.ToByte(msg.OtherCarId), out otherDriver)) { throw new Exception("(Other) Driver not found: " + msg.OtherCarId); } var driversCache = GetDriversCache(driver, 980); var otherDriversCache = GetDriversCache(otherDriver, 980); var driversDistance = _distancesToReport[driver]; _distancesToReport[driver] = new MRDistanceHelper(); TrySendDistance(driver, true); MRBackend.CollisionAsyncV22(msg.CreationDate, msg.CarId, msg.OtherCarId, msg.RelativeVelocity, driver.LastSplinePosition, msg.RelativePosition.X, msg.RelativePosition.Z, msg.WorldPosition.X, msg.WorldPosition.Z, driversCache.ToArray(), otherDriversCache.ToArray(), driversDistance); } }
private void TrySendDistance(DriverInfo di, bool forced = false) { if (!_distancesToReport.ContainsKey(di)) { _distancesToReport.Add(di, new MRDistanceHelper()); } // New approach: We'll send the distance set as soon as a driver crosses a TrackDefinition.Split #region legacy approach: fixed distance if (CurrentTrackDefinition == null || CurrentTrackDefinition.Splits == null) { var distanceCached = _distancesToReport[di]; // Then we'll do it in different resolutions; the first meters are more important than the later ones if (di.Distance > REGULAR_DISTANCE && distanceCached.MetersDriven > 2000 || forced) // After 2km, we'll just report in big chunks - or if forced { MRBackend.DistanceDrivenAsync(di.CarId, distanceCached); _distancesToReport[di] = new MRDistanceHelper(); } else if (di.Distance < REGULAR_DISTANCE && distanceCached.MetersDriven > 200) // 200m is about "left pits", so we'll report this until { MRBackend.DistanceDrivenAsync(di.CarId, distanceCached); _distancesToReport[di] = new MRDistanceHelper(); } } #endregion else { try { if (di.LastCarUpdate != null && di.LastCarUpdate.List.Count > 1) { var lastPos = di.LastCarUpdate.Previous.Value; var thisPos = di.LastCarUpdate.Value; foreach (var split in CurrentTrackDefinition.Splits) { if (lastPos.NormalizedSplinePosition < split && thisPos.NormalizedSplinePosition > split) { // Gotcha! var distanceCached = _distancesToReport[di]; _distancesToReport[di] = new MRDistanceHelper(); distanceCached.SplinePosCurrent = thisPos.NormalizedSplinePosition; distanceCached.SplinePosTimeCurrent = Convert.ToInt32(thisPos.CreationDate.Subtract(di.CurrentLapStart).TotalMilliseconds); distanceCached.SplinePosLast = lastPos.NormalizedSplinePosition; distanceCached.SplinePosTimeLast = Convert.ToInt32(lastPos.CreationDate.Subtract(di.CurrentLapStart).TotalMilliseconds); MRBackend.DistanceDrivenAsync(di.CarId, distanceCached); break; } } } } catch (Exception ex) { PluginManager.Log(ex); } } }
protected override void OnCarInfo(MsgCarInfo msg) { PluginManager.Log(DateTime.Now.TimeOfDay.ToString() + "- CarInfo: " + msg.CarId + ", " + msg.DriverName + "@" + msg.CarModel + ", Connected=" + msg.IsConnected); // To prevent a bug in communication we will only send when the Car IsConnected - discos only via the corresponding event please. if (msg.IsConnected) { MRBackend.RandomCarInfoAsync(msg.CarId, msg.CarModel, msg.DriverName, msg.DriverGuid, msg.IsConnected, GetCurrentRaceTimeMS(msg)); } }
protected override void OnLapCompleted(MsgLapCompleted msg) { DriverInfo driver; if (!PluginManager.TryGetDriverInfo(msg.CarId, out driver)) { PluginManager.Log("Error; car_id " + msg.CarId + " was not known by the PluginManager :("); } else { driver.IsOnOutlap = false; TrySendDistance(driver, true); MRBackend.LapCompletedAsync(msg.CreationDate, msg.CarId, driver.DriverGuid, msg.Laptime, msg.Cuts, msg.GripLevel, ConvertLB(msg.Leaderboard)); } }
private void ReloadTrackDefinition(string trackId = null) { if (trackId == null) { trackId = CurrentTrackDefinition?.TrackName; } if (trackId != null) { CurrentTrackDefinition = MRBackend.GetTrackDefinition(trackId); CreatePitExitRectangle(); PluginManager.Log($"Pit exit rectangle created: {PitExitRectangle}"); PluginManager.Log($"Track Lines parsed: {CurrentTrackDefinition?.Lines?.Length ?? 0}"); } }
protected override void OnAcServerAlive() { MRBackend.SendAlive(GetConnectedDriversHash()); }
protected override void OnCarUpdate(DriverInfo di) { #region Distance if (!_distancesToReport.ContainsKey(di)) { _distancesToReport.Add(di, new MRDistanceHelper()); } var dh = _distancesToReport[di]; // Generally, the meters driven are stored dh.MetersDriven += di.LastDistanceTraveled; // To protect this from some simple 1st gear driving together in combat range to grind stuff, we'll only allow Attack & Combat range // recording if there is acceleration. 3 or 5 are quite little values, even for slow cars like the GT86 if (Math.Abs(di.CurrentAcceleration) > 2.0f && di.CurrentDistanceToClosestCar != 0) { // Then we'll check this interval (we're talking about a second or similar) // for driving in attack range (let's say.. inside 20m) or even combating (maybe 8m) if (di.CurrentDistanceToClosestCar < 8) { dh.MetersCombatRange += di.LastDistanceTraveled; } else if (di.CurrentDistanceToClosestCar < 20) { dh.MetersAttackRange += di.LastDistanceTraveled; } } #endregion #region Outlap detection if (!di.IsOnOutlap && PitExitRectangle.HasValue) { di.IsOnOutlap = PitExitRectangle.Value.Contains(new System.Windows.Point(di.LastPosition.X, di.LastPosition.Z)); if (di.IsOnOutlap) { MRBackend.DriverBackToPitsAsync(di.CarId, DateTime.Now); } } #endregion #region teleported to pits detection if (TeleportedToPits(di)) { // Special trick: If we assume a teleport, we set the outlap to false // so the outlap detection above can trigger again di.IsOnOutlap = false; } #endregion #region Line crossing try { if (CurrentTrackDefinition?.Lines != null && di.LastCarUpdate?.Previous != null) { // For performance we'll just calc the lines that are between the corresponding SplinePosition frame foreach (var l in CurrentTrackDefinition.Lines.Where(x => x.FromSpline <= di.LastSplinePosition && x.ToSpline >= di.LastSplinePosition)) { var isIntersecting = IsIntersecting(l, di.LastCarUpdate); //PluginManager.BroadcastChatMessage($"{l.LineId}: {isIntersecting}"); // We are between the focus zone for this (l)ine. Did we cross it? if (isIntersecting) { // That's worth a message to the backend then! // Just need the min and max velocity of the latest entries var driversCache = GetDriversCache(di, 98); var driversVelocities = driversCache.Select(x => new Vector3f(x.Velocity[0], 0, x.Velocity[1]).Length() * 3.6f); var worldPositions = new List <float>(); foreach (var item in driversCache.Select(x => x.WorldPosition)) { worldPositions.AddRange(item); } var distanceToNextCar = di.CurrentDistanceToClosestCar; if (distanceToNextCar == 0) // = no other cars around { distanceToNextCar = 99999; } MRBackend.LineCrossedAsync(di.CarId, l.LineId, di.CurrentSpeed, di.CurrentAcceleration, driversVelocities.Min(), driversVelocities.Max(), distanceToNextCar, worldPositions.ToArray()); } } } } catch (Exception ex) { PluginManager.Log(ex); } #endregion }
protected override void OnAcServerTimeout() { PluginManager.Log("OnAcServerTimeout()"); MRBackend.EndSessionAsync(); }
protected override void OnClientLoaded(MsgClientLoaded msg) { MRBackend.RequestDriverLoadedAsync(msg.CarId); }
protected override void OnChatMessage(MsgChat msg) { if (!msg.IsCommand) { return; } var split = msg.Message.Split(' '); if (split.Length > 0) { switch (split[0].ToLower()) { case "/mr": case "/minorating": { if (split.Length == 1) // only /mr { MRBackend.RequestDriverRatingAsync(msg.CarId); } else { MRBackend.RequestMRCommandAdminInfoAsync(msg.CarId, PluginManager.GetDriverInfo(msg.CarId).IsAdmin, split); } } break; case "/mrpoint": { string text; DriverInfo driver = null; if (!PluginManager.TryGetDriverInfo(Convert.ToByte(msg.CarId), out driver)) { text = "Driver not found: " + msg.CarId; } else if (driver?.LastCarUpdate?.Value == null) { text = "Something's wrong"; } else { var upd = driver?.LastCarUpdate?.Value; text = $"Spl:{upd.NormalizedSplinePosition:F5}|X={upd.WorldPosition.X:F5}|Z={upd.WorldPosition.Z:F5}"; } PluginManager.SendChatMessage(msg.CarId, text); } break; case "/mrinfo": { PluginManager.SendChatMessage(msg.CarId, $"Track id: {CurrentTrackDefinition?.TrackName}, length ={CurrentTrackDefinition?.Length:N0}"); PluginManager.SendChatMessage(msg.CarId, $"Pit exit: {PitExitRectangle?.X}"); } break; case "/mrtrackreload": { CurrentTrackDefinition = MRBackend.GetTrackDefinition(CurrentTrackDefinition.TrackName); CreatePitExitRectangle(); PluginManager.SendChatMessage(msg.CarId, $"Track reloaded"); } break; case "/mrtl1": { string text; DriverInfo driver = null; if (!PluginManager.TryGetDriverInfo(Convert.ToByte(msg.CarId), out driver)) { text = "Driver not found: " + msg.CarId; } else if (driver?.LastCarUpdate?.Value == null) { text = "Something's wrong"; } else { _AdminAddTrackLineStart = driver?.LastCarUpdate?.Value; text = $"Start set: {_AdminAddTrackLineStart}"; } PluginManager.SendChatMessage(msg.CarId, text); } break; case "/mrtl2": { Console.WriteLine("TrackLine 2"); string text = null; int type = 2; // pit exit = 1, default is line = 2 DriverInfo driver = null; if (_AdminAddTrackLineStart == null) { text = "No start set"; } else if (!PluginManager.TryGetDriverInfo(Convert.ToByte(msg.CarId), out driver)) { text = "Driver not found: " + msg.CarId; } else if (driver?.LastCarUpdate?.Value == null) { text = "Something's wrong"; } else if (split.Length < 2 || string.IsNullOrEmpty(split[1])) { text = "No hint set"; } else { if (split.Length == 3) { type = int.Parse(split[2]); } try { var startPoint = _AdminAddTrackLineStart; var endPoint = driver?.LastCarUpdate?.Value; MRBackend.CreateTrackLine(msg.CarId, startPoint.NormalizedSplinePosition, endPoint.NormalizedSplinePosition, startPoint.WorldPosition.X, startPoint.WorldPosition.Z, endPoint.WorldPosition.X, endPoint.WorldPosition.Z, split[1], type); Console.WriteLine($"Message sent, type {type}"); ReloadTrackDefinition(); PluginManager.SendChatMessage(msg.CarId, "Track reloaded"); } catch (Exception ex) { Console.WriteLine($"Exception in TrackLine2: {ex.ToString()}"); } } if (!string.IsNullOrEmpty(text)) { PluginManager.SendChatMessage(msg.CarId, text); } } break; default: break; } } }
protected override void OnConnectionClosed(MsgConnectionClosed msg) { MRBackend.RandomCarInfoAsync(msg.CarId, "", "", "", false, GetCurrentRaceTimeMS(msg)); }
protected override void OnSessionEnded(MsgSessionEnded msg) { PluginManager.Log("Session ended"); MRBackend.EndSessionAsync(); }
protected override void OnNewConnection(MsgNewConnection msg) { MRBackend.RandomCarInfoAsync(msg.CarId, msg.CarModel, msg.DriverName, msg.DriverGuid, true, GetCurrentRaceTimeMS(msg)); }