Beispiel #1
0
            private IEnumerable <string> GetDetails(int currentIndex, CarObject car, CarSkinObject skin, int?currentEntrySkinsLeft)
            {
                var left = LeftSkins(currentIndex) + (currentEntrySkinsLeft ?? _approximateSkinsPerCarSkins / _approximateSkinsPerCarCars);

                var speed          = _shotSkins / _started.Elapsed.TotalMinutes;
                var remainingTime  = speed < 0.0001 ? "Unknown" : $"About {TimeSpan.FromMinutes(left / speed).ToReadableTime()}";
                var remainingItems = $"About {left} {PluralizingConverter.Pluralize(left, ControlsStrings.CustomShowroom_SkinHeader).ToSentenceMember()}";

                return(new[] {
                    $"Car: {car?.DisplayName}", $"Skin: {skin?.DisplayName ?? "?"}",
                    $"Speed: {speed:F2} {PluralizingConverter.Pluralize(10, ControlsStrings.CustomShowroom_SkinHeader).ToSentenceMember()}/{"min"}",
                    $"Time remaining: {remainingTime}",
                    $"Items remaining: {remainingItems}",
                    _recyclingWarning ? "[i]Recycling seems to take too long? If so, it can always be disabled in Settings.[/i]" : null
                }.NonNull());
            }
Beispiel #2
0
        private static BaseFinishedData GetFinishedData(Game.StartProperties properties, Game.Result result)
        {
            var conditions = properties?.GetAdditional <PlaceConditions>();
            var takenPlace = conditions?.GetTakenPlace(result) ?? PlaceConditions.UnremarkablePlace;

            Logging.Debug($"Place conditions: {conditions?.GetDescription()}, result: {result.GetDescription()}");

            if (result.GetExtraByType <Game.ResultExtraDrift>(out var drift))
            {
                return(new DriftFinishedData {
                    Points = drift.Points,
                    MaxCombo = drift.MaxCombo,
                    MaxLevel = drift.MaxLevel,
                    TakenPlace = takenPlace
                });
            }

            if (result.GetExtraByType <Game.ResultExtraTimeAttack>(out var timeAttack))
            {
                var bestLapTime = result.Sessions?.SelectMany(x => from lap in x.BestLaps where lap.CarNumber == 0 select lap.Time).MinOrDefault();
                return(new TimeAttackFinishedData {
                    Points = timeAttack.Points,
                    Laps = result.Sessions?.Sum(x => x.LapsTotalPerCar?.FirstOrDefault() ?? 0) ?? 0,
                    BestLapTime = bestLapTime == TimeSpan.Zero ? null : bestLapTime,
                    TakenPlace = takenPlace
                });
            }

            if (result.GetExtraByType <Game.ResultExtraBestLap>(out var bestLap) && bestLap.IsNotCancelled && result.Sessions?.Length == 1 &&
                result.Players?.Length == 1)
            {
                var bestLapTime = result.Sessions.SelectMany(x => from lap in x.BestLaps where lap.CarNumber == 0 select lap.Time).MinOrDefault();

                var sectorsPerSections  = result.Sessions.SelectMany(x => from lap in x.Laps where lap.CarNumber == 0 select lap.SectorsTime).ToList();
                var theoreticallLapTime = sectorsPerSections.FirstOrDefault()?.Select((x, i) => sectorsPerSections.Select(y => y[i]).Min()).Sum();

                return(result.Sessions[0].Name == AppStrings.Rsr_SessionName ? new RsrFinishedData {
                    Laps = result.Sessions.Sum(x => x.LapsTotalPerCar?.FirstOrDefault() ?? 0),
                    BestLapTime = bestLapTime == TimeSpan.Zero ? (TimeSpan?)null : bestLapTime,
                    TheoreticallLapTime = theoreticallLapTime,
                    TakenPlace = takenPlace
                } : new HotlapFinishedData {
                    Laps = result.Sessions.Sum(x => x.LapsTotalPerCar?.FirstOrDefault() ?? 0),
                    BestLapTime = bestLapTime == TimeSpan.Zero ? (TimeSpan?)null : bestLapTime,
                    TheoreticallLapTime = theoreticallLapTime,
                    TakenPlace = takenPlace
                });
            }

            var isOnline   = properties?.ModeProperties is Game.OnlineProperties;
            var playerName = isOnline && SettingsHolder.Drive.DifferentPlayerNameOnline
                    ? SettingsHolder.Drive.PlayerNameOnline : SettingsHolder.Drive.PlayerName;

            if (result.Sessions?.Length == 1 && result.Sessions[0].Name == Game.TrackDaySessionName && result.Players?.Length > 1)
            {
                var session    = result.Sessions[0];
                var playerLaps = session.LapsTotalPerCar?[0] ?? 1;
                session.LapsTotalPerCar = session.LapsTotalPerCar?.Select(x => Math.Min(x, playerLaps)).ToArray();
                session.Laps            = session.Laps?.Where(x => x.LapId < playerLaps).ToArray();
                session.BestLaps        = session.BestLaps?.Select(x => {
                    if (x.LapId < playerLaps)
                    {
                        return(x);
                    }
                    var best = session.Laps?.Where(y => y.CarNumber == x.CarNumber).MinEntryOrDefault(y => y.Time);
                    if (best == null)
                    {
                        return(null);
                    }
                    return(new Game.ResultBestLap {
                        Time = best.Time,
                        LapId = best.LapId,
                        CarNumber = x.CarNumber
                    });
                }).NonNull().ToArray();
            }

            var dragExtra    = result.GetExtraByType <Game.ResultExtraDrag>();
            var sessionsData = result.Sessions?.Select(session => {
                int[] takenPlaces;
                SessionFinishedData data;

                if (dragExtra != null)
                {
                    data = new DragFinishedData {
                        BestReactionTime = dragExtra.ReactionTime,
                        Total            = dragExtra.Total,
                        Wins             = dragExtra.Wins,
                        Runs             = dragExtra.Runs,
                    };

                    var delta   = dragExtra.Wins * 2 - dragExtra.Runs;
                    takenPlaces = new[] {
                        delta >= 0 ? 0 : 1,
                        delta <= 0 ? 0 : 1
                    };
                }
                else
                {
                    data        = new SessionFinishedData(session.Name?.ApartFromLast(@" Session"));
                    takenPlaces = session.GetTakenPlacesPerCar();
                }

                var sessionBestLap = session.BestLaps?.MinEntryOrDefault(x => x.Time);
                var sessionBest    = sessionBestLap?.Time;

                data.PlayerEntries = (
                    from player in result.Players
                    let car = CarsManager.Instance.GetById(player.CarId ?? "")
                              let carSkin = car?.GetSkinById(player.CarSkinId ?? "")
                                            select new { Player = player, Car = car, CarSkin = carSkin }
                    ).Select((entry, i) => {
                    var bestLapTime = session.BestLaps?.Where(x => x.CarNumber == i).MinEntryOrDefault(x => x.Time)?.Time;
                    var laps        = session.Laps?.Where(x => x.CarNumber == i).Select(x => new SessionFinishedData.PlayerLapEntry {
                        LapNumber          = x.LapId + 1,
                        Sectors            = x.SectorsTime,
                        Cuts               = x.Cuts,
                        TyresShortName     = x.TyresShortName,
                        Total              = x.Time,
                        DeltaToBest        = bestLapTime.HasValue ? x.Time - bestLapTime.Value : (TimeSpan?)null,
                        DeltaToSessionBest = sessionBest.HasValue ? x.Time - sessionBest.Value : (TimeSpan?)null,
                    }).ToArray();

                    var lapTimes = laps?.Skip(1).Select(x => x.Total.TotalSeconds).Where(x => x > 10d).ToList();
                    double?progress, spread;
                    if (lapTimes == null || lapTimes.Count < 2)
                    {
                        progress = null;
                        spread   = null;
                    }
                    else
                    {
                        var firstHalf    = lapTimes.Count / 2;
                        var firstMedian  = lapTimes.Take(firstHalf).Median();
                        var secondMedian = lapTimes.Skip(firstHalf).Median();
                        progress         = 1d - secondMedian / firstMedian;
                        spread           = (lapTimes.Max() - lapTimes.Min()) / bestLapTime?.TotalSeconds;
                    }

                    return(new SessionFinishedData.PlayerEntry {
                        Name = i == 0 ? playerName : entry.Player.Name,
                        IsPlayer = i == 0,
                        Index = i,
                        Car = entry.Car,
                        CarSkin = entry.CarSkin,
                        TakenPlace = i < takenPlaces?.Length ? takenPlaces[i] + 1 : DefinitelyNonPrizePlace,
                        PrizePlace = takenPlaces?.Length > 1,
                        LapsCount = session.LapsTotalPerCar?.ArrayElementAtOrDefault(i) ?? 0,
                        BestLapTime = bestLapTime,
                        DeltaToSessionBest = sessionBestLap?.CarNumber == i ? null : bestLapTime - sessionBest,
                        TotalTime = session.Laps?.Where(x => x.CarNumber == i).Select(x => x.SectorsTime.Sum()).Sum(),
                        Laps = laps,
                        LapTimeProgress = progress,
                        LapTimeSpread = spread
                    });
                }).OrderBy(x => x.TakenPlace).ToList();

                if (data.PlayerEntries.Count > 0)
                {
                    var maxLaps       = data.PlayerEntries.Select(x => x.Laps.Length).Max();
                    var bestTotalTime = data.PlayerEntries.Where(x => x.Laps.Length == maxLaps).MinEntryOrDefault(x => x.TotalTime ?? TimeSpan.MaxValue);
                    foreach (var entry in data.PlayerEntries)
                    {
                        var lapsDelta = maxLaps - entry.Laps.Length;
                        if (bestTotalTime == entry)
                        {
                            entry.TotalTimeDelta = "-";
                        }
                        else if (lapsDelta > 0)
                        {
                            entry.TotalTimeDelta = $"+{lapsDelta} {PluralizingConverter.Pluralize(lapsDelta, "lap")}";
                        }
                        else
                        {
                            var t = entry.TotalTime - bestTotalTime?.TotalTime;
                            var v = t?.ToMillisecondsString();
                            entry.TotalTimeDelta = t == TimeSpan.Zero ? "" : v != null ? $"+{v}" : null;
                        }
                    }
                }

                var bestProgress = (from player in data.PlayerEntries
                                    where player.LapTimeProgress > 0.03
                                    orderby player.LapTimeProgress descending
                                    select player).FirstOrDefault();
                var bestConsistent = (from player in data.PlayerEntries
                                      where player.LapTimeSpread < 0.02
                                      orderby player.LapTimeSpread descending
                                      select player).FirstOrDefault();

                data.RemarkableNotes = new[] {
                    sessionBestLap == null ? null :
                    new SessionFinishedData.RemarkableNote("[b]Best lap[/b] made by ", data.PlayerEntries.GetByIdOrDefault(sessionBestLap.CarNumber),
                                                           null),
                    new SessionFinishedData.RemarkableNote("[b]The Best Off-roader Award[/b] goes to ", (from player in data.PlayerEntries
                                                                                                         let cuts =
                                                                                                             (double)player.Laps.Sum(x => x.Cuts)
                                                                                                             / player.Laps.Length
                                                                                                             where cuts > 1.5
                                                                                                             orderby cuts descending
                                                                                                             select player).FirstOrDefault(), null),
                    new SessionFinishedData.RemarkableNote("[b]Remarkable progress[/b] shown by ", bestProgress, null),
                    new SessionFinishedData.RemarkableNote("[b]The most consistent[/b] is ", bestConsistent, null),
                }.Where(x => x?.Player != null).ToList();
                return(data);
            }).ToList();

            return(sessionsData?.Count == 1 ? (BaseFinishedData)sessionsData.First() :
                   sessionsData?.Any() == true ? new SessionsFinishedData(sessionsData) : null);
        }
Beispiel #3
0
        private static async Task <IReadOnlyList <UpdatePreviewError> > UpdatePreview(IReadOnlyList <ToUpdatePreview> entries, DarkPreviewsOptions options, string presetName = null,
                                                                                      DarkPreviewsUpdater updater = null)
        {
            var localUpdater = updater == null;

            if (localUpdater)
            {
                updater = new DarkPreviewsUpdater(AcRootDirectory.Instance.RequireValue, options);
            }
            else
            {
                updater.SetOptions(options);
            }

            var errors = new List <UpdatePreviewError>();

            try {
                if (options.Showroom != null && ShowroomsManager.Instance.GetById(options.Showroom) == null)
                {
                    if (options.Showroom == "at_previews" && MissingShowroomHelper != null)
                    {
                        await MissingShowroomHelper.OfferToInstall("Kunos Previews Showroom (AT Previews Special)", "at_previews",
                                                                   "http://www.assettocorsa.net/assetto-corsa-v1-5-dev-diary-part-33/");

                        if (ShowroomsManager.Instance.GetById(options.Showroom) != null)
                        {
                            goto Action;
                        }
                    }

                    throw new InformativeException("Can’t update preview", $"Showroom “{options.Showroom}” is missing");
                }

Action:
                var checksum = options.GetChecksum();

                var finished = false;
                var j        = 0;

                using (var waiting = new WaitingDialog()) {
                    var cancellation = waiting.CancellationToken;

                    var singleMode     = entries.Count == 1;
                    var verySingleMode = singleMode && entries[0].Skins?.Count == 1;
                    var recycled       = 0;

                    if (!verySingleMode)
                    {
                        waiting.SetImage(null);

                        if (SettingsHolder.CustomShowroom.PreviewsRecycleOld)
                        {
                            waiting.SetMultiline(true);
                        }
                    }

                    var step    = 1d / entries.Count;
                    var postfix = string.Empty;

                    var started = Stopwatch.StartNew();
                    var approximateSkinsPerCarCars  = 1;
                    var approximateSkinsPerCarSkins = 10;

                    Action <int> updateApproximate = skinsPerCar => {
                        approximateSkinsPerCarCars++;
                        approximateSkinsPerCarSkins += skinsPerCar;
                    };

                    Func <int, int> leftSkins = currentEntry => {
                        var skinsPerCar = (double)approximateSkinsPerCarSkins / approximateSkinsPerCarCars;

                        var result = 0d;
                        for (var k = currentEntry; k < entries.Count; k++)
                        {
                            var entry = entries[k];
                            result += entry.Skins?.Count ?? skinsPerCar;
                        }

                        return(result.RoundToInt());
                    };

                    var shotSkins        = 0;
                    var recyclingWarning = false;
                    Func <CarObject, CarSkinObject, int?, IEnumerable <string> > getDetails = (car, skin, currentEntrySkinsLeft) => {
                        var left = leftSkins(j) + (currentEntrySkinsLeft ?? approximateSkinsPerCarSkins / approximateSkinsPerCarCars);

                        // ReSharper disable once AccessToModifiedClosure
                        var speed          = shotSkins / started.Elapsed.TotalMinutes;
                        var remainingTime  = speed < 0.0001 ? "Unknown" : $"About {TimeSpan.FromMinutes(left / speed).ToReadableTime()}";
                        var remainingItems = $"About {left} {PluralizingConverter.Pluralize(left, ControlsStrings.CustomShowroom_SkinHeader).ToSentenceMember()}";

                        return(new[] {
                            $"Car: {car?.DisplayName}",
                            $"Skin: {skin?.DisplayName ?? "?"}",
                            $"Speed: {speed:F2} {PluralizingConverter.Pluralize(10, ControlsStrings.CustomShowroom_SkinHeader).ToSentenceMember()}/{"min"}",
                            $"Time remaining: {remainingTime}",
                            $"Items remaining: {remainingItems}",

                            // ReSharper disable once AccessToModifiedClosure
                            recyclingWarning ? "[i]Recycling seems to take too long? If so, it can always be disabled in Settings.[/i]" : null
                        }.NonNull());
                    };

                    for (j = 0; j < entries.Count; j++)
                    {
                        if (cancellation.IsCancellationRequested)
                        {
                            goto Cancel;
                        }

                        var entry    = entries[j];
                        var progress = step * j;

                        var car   = entry.Car;
                        var skins = entry.Skins;

                        if (skins == null)
                        {
                            waiting.Report(new AsyncProgressEntry("Loading skins…" + postfix, verySingleMode ? 0d : progress));
                            waiting.SetDetails(getDetails(car, null, null));

                            await car.SkinsManager.EnsureLoadedAsync();

                            if (cancellation.IsCancellationRequested)
                            {
                                goto Cancel;
                            }

                            skins = car.EnabledOnlySkins.ToList();
                            updateApproximate(skins.Count);
                        }

                        var halfstep = step * 0.5 / skins.Count;
                        for (var i = 0; i < skins.Count; i++)
                        {
                            if (cancellation.IsCancellationRequested)
                            {
                                goto Cancel;
                            }

                            var skin = skins[i];
                            waiting.SetDetails(getDetails(car, skin, skins.Count - i));

                            var subprogress = progress + step * (0.1 + 0.8 * i / skins.Count);
                            if (SettingsHolder.CustomShowroom.PreviewsRecycleOld && File.Exists(skin.PreviewImage))
                            {
                                if (++recycled > 5)
                                {
                                    recyclingWarning = true;
                                }

                                waiting.Report(new AsyncProgressEntry($"Recycling current preview for {skin.DisplayName}…" + postfix, verySingleMode ? 0d : subprogress));
                                await Task.Run(() => FileUtils.Recycle(skin.PreviewImage));
                            }

                            waiting.Report(new AsyncProgressEntry($"Updating skin {skin.DisplayName}…" + postfix, verySingleMode ? 0d : subprogress + halfstep));

                            try {
                                await updater.ShotAsync(car.Id, skin.Id, skin.PreviewImage, car.AcdData, GetInformation(car, skin, presetName, checksum),
                                                        () => {
                                    if (!verySingleMode)
                                    {
                                        ActionExtension.InvokeInMainThreadAsync(() => {
                                            // ReSharper disable once AccessToModifiedClosure
                                            if (!finished)
                                            {
                                                // ReSharper disable once AccessToDisposedClosure
                                                waiting.SetImage(skin.PreviewImage);
                                            }
                                        });
                                    }
                                });

                                shotSkins++;
                            } catch (Exception e) {
                                if (errors.All(x => x.ToUpdate != entry))
                                {
                                    errors.Add(new UpdatePreviewError(entry, e.Message, null));
                                }
                            }
                        }
                    }

                    waiting.Report(new AsyncProgressEntry("Saving…" + postfix, verySingleMode ? 0d : 0.999999d));
                    await updater.WaitForProcessing();

                    finished = true;
                }

                if (errors.Count > 0)
                {
                    NonfatalError.Notify("Can’t update previews:\n" + errors.Select(x => @"• " + x.Message.ToSentence()).JoinToString(";" + Environment.NewLine));
                }

                goto End;

Cancel:
                for (; j < entries.Count; j++)
                {
                    errors.Add(new UpdatePreviewError(entries[j], ControlsStrings.Common_Cancelled, null));
                }

End:
                return(errors);
            } catch (Exception e) {
                NonfatalError.Notify("Can’t update preview", e);
                return(null);
            } finally {
                if (localUpdater)
                {
                    updater.Dispose();
                    GC.Collect();
                }
            }
        }