public async Task Update(UpdateMode mode, bool background = false, bool fast = false) { if (_updating) { return; } _updating = true; _pingId++; var driversCount = -1; var resultStatus = ServerStatus.Ready; try { // If it’s a background update, don’t change anything in UI to avoid flashing if (!background) { CurrentDrivers = null; Status = ServerStatus.Loading; IsAvailable = false; } // Reset some update-state values PrepareErrorsList(); // Nothing loaded at all! var informationUpdated = false; if (!IsFullyLoaded) { UpdateProgress = new AsyncProgressEntry(ToolsStrings.Online_LoadingActualInformation, 0.1); ServerInformationComplete loaded; try { loaded = await GetInformationDirectly(); } catch (HttpRequestException e) { if (e.InnerException is WebException webException) { _updateWebException = webException; } else { _updateException = e; } resultStatus = ServerStatus.Error; return; } catch (WebException e) { _updateWebException = e; resultStatus = ServerStatus.Error; return; } var update = UpdateValues(loaded, false, true); if (update != null) { resultStatus = update.Value; if (update != ServerStatus.MissingContent) { // Loaded data isn’t for this server (port by which it was loaded differs). // Won’t even set drivers count in this case, whole data is obviously wrong. return; } } driversCount = loaded.Clients; // Set this flag to True so we won’t use GetInformationDirectly() again later informationUpdated = true; } // Extended mode for server wrapping thing var informationLoadedExtended = false; ServerCarsInformation carsInformation = null; if (DetailsPort != null) { try { var extended = await GetExtendedInformationDirectly(); var update = UpdateValues(extended, false, true); if (update != null) { resultStatus = update.Value; if (update != ServerStatus.MissingContent) { UpdateValuesExtended(null); return; } } UpdateValuesExtended(extended); driversCount = extended.Clients; carsInformation = extended.Players; informationLoadedExtended = true; } catch (Exception e) { Logging.Warning(e); DetailsPort = null; UpdateValuesExtended(null); return; } } else if (DetailsId != null) { try { var extended = await CmApiProvider.GetOnlineDataAsync(DetailsId); if (extended != null) { Country = extended.Country?.FirstOrDefault() ?? Country; CountryId = extended.Country?.ArrayElementAtOrDefault(1) ?? CountryId; Sessions?.ForEach((x, i) => x.Duration = extended.Durations?.ElementAtOrDefault(i) ?? x.Duration); } UpdateValuesExtended(extended); } catch (Exception e) { Logging.Warning(e); UpdateValuesExtended(null); return; } } else { UpdateValuesExtended(null); } // Update information if (!informationLoadedExtended && (mode != UpdateMode.Lite || !(Sessions?.Count > 0) // if there are no sessions (!), maybe information is damaged, let’s re-download )) { UpdateProgress = new AsyncProgressEntry(ToolsStrings.Online_LoadingActualInformation, 0.2); ServerInformationComplete loaded; try { // If informationUpdated is True and settings set to update-information-directly mode, this method // will return 0. loaded = await GetInformation(informationUpdated); } catch (WebException e) { _updateWebException = e; resultStatus = ServerStatus.Error; return; } if (loaded != null) { if (loaded.Ip == Ip && loaded.PortHttp == PortHttp || informationUpdated || loaded.LoadedDirectly) { // If loaded information is compatible with existing, use it immediately. Otherwise — apparently, // server changed — we’ll try to load an actual data directly from it later, but only if it wasn’t // loaded just before that and loaded information wasn’t loaded from it. var update = UpdateValues(loaded, false, true); if (update != null) { resultStatus = update.Value; if (update != ServerStatus.MissingContent) { return; } } driversCount = loaded.Clients; } else { ServerInformation directlyLoaded; try { directlyLoaded = await GetInformationDirectly(); } catch (WebException e) { _updateWebException = e; resultStatus = ServerStatus.Error; return; } var update = UpdateValues(directlyLoaded, false, true); if (update != null) { resultStatus = update.Value; if (update != ServerStatus.MissingContent) { return; } } driversCount = loaded.Clients; } } } // Load players list if (carsInformation == null) { UpdateProgress = new AsyncProgressEntry(ToolsStrings.Online_LoadingPlayersList, 0.4); try { carsInformation = await KunosApiProvider.GetCarsInformationAsync(Ip, PortHttp); } catch (WebException e) { _updateWebException = e; resultStatus = ServerStatus.Error; return; } } if (!BookingMode) { CurrentDriversCount = carsInformation.Cars.Count(x => x.IsConnected); driversCount = -1; } var currentDrivers = (BookingMode ? carsInformation.Cars : carsInformation.Cars.Where(x => x.IsConnected)) .Select(x => { var driver = CurrentDrivers?.FirstOrDefault(y => y.SameAs(x)) ?? new CurrentDriver(x, RequiredCspVersion != 0); return(driver); }) .ToList(); if (CurrentDrivers == null || !CurrentDrivers.SequenceEqual(currentDrivers)) { CurrentDrivers = currentDrivers; var count = 0; var booked = false; foreach (var x in currentDrivers) { if (x.IsConnected) { count++; } if (x.IsBookedForPlayer) { booked = true; SetSelectedCarEntry(Cars?.GetByIdOrDefault(x.CarId, StringComparison.OrdinalIgnoreCase)); } } ConnectedDrivers = count; IsBookedForPlayer = booked; } if (Cars == null) { Logging.Unexpected(); _updateDriversMissing = true; resultStatus = ServerStatus.Error; return; } for (int i = 0, c = Cars.Count; i < c; i++) { var entry = Cars[i]; var wrapper = entry.CarWrapper; CarObject car; // Load car if not loaded if (wrapper != null) { if (wrapper.IsLoaded) { car = (CarObject)wrapper.Value; } else { UpdateProgress = new AsyncProgressEntry(string.Format(ToolsStrings.Online_LoadingCars, wrapper.Id), 0.5 + 0.4 * i / c); await Task.Delay(fast? 10 : 50); car = (CarObject)await wrapper.LoadedAsync(); } car.SubscribeWeak(OnContentNameChanged); } else { car = null; } // Load skin if (car?.SkinsManager.IsLoaded == false) { UpdateProgress = new AsyncProgressEntry(string.Format(ToolsStrings.Online_LoadingSkins, car.DisplayName), 0.5 + 0.4 * (0.5 + i) / c); await Task.Delay(fast? 10 : 50); await car.SkinsManager.EnsureLoadedAsync(); } // Set next available skin if (CurrentSessionType == Game.SessionType.Booking) { entry.SetAvailableSkinId(car?.SelectedSkin?.Id, null); entry.Total = 0; entry.Available = 0; entry.IsAvailable = true; } else { var cars = carsInformation.Cars.Where(x => x.IsEntryList && string.Equals(x.CarId, entry.Id, StringComparison.OrdinalIgnoreCase)).ToList(); ServerActualCarInformation availableSkin; if (BookingMode) { availableSkin = cars.FirstOrDefault(x => x.IsRequestedGuid); entry.Total = 0; entry.Available = 0; entry.IsAvailable = true; } else { availableSkin = cars.FirstOrDefault(y => !y.IsConnected); entry.Total = cars.Count; entry.Available = cars.Count(y => !y.IsConnected); entry.IsAvailable = entry.Available > 0; } entry.SetAvailableSkinId(availableSkin?.CarSkinId, RequiredCspVersion == 0 ? null : availableSkin?.CspParams); } } var missingContentUpdate = UpdateMissingContentExtended(resultStatus == ServerStatus.MissingContent); if (missingContentUpdate.HasValue) { resultStatus = missingContentUpdate.Value; } if (IsBookedForPlayer) { FixedCar = true; } else { FixedCar = false; LoadSelectedCar(); } // Ping server if (Ping == null || mode == UpdateMode.Full || !SettingsHolder.Online.PingOnlyOnce) { if (mode == UpdateMode.Lite) { await TryToPing(); } else { TryToPing().Ignore(); } } } catch (Exception e) { _updateException = e; resultStatus = ServerStatus.Error; Logging.Error(e); } finally { if (driversCount != -1) { CurrentDriversCount = driversCount; } UpdateProgress = AsyncProgressEntry.Ready; Status = !SettingsHolder.Online.LoadServersWithMissingContent && resultStatus == ServerStatus.MissingContent ? ServerStatus.Error : resultStatus; UpdateMissingContent(); UpdateErrorsList(); AvailableUpdate(); _updating = false; } }
public async Task Update(UpdateMode mode, bool background = false, bool fast = false) { if (_updating) { return; } _updating = true; var errors = new List <string>(3); var driversCount = -1; try { if (!background) { CurrentDrivers = null; Status = ServerStatus.Loading; IsAvailable = false; } var informationUpdated = false; if (!IsFullyLoaded) { UpdateProgress = new AsyncProgressEntry("Loading actual server information…", 0.1); ServerInformationComplete loaded; try { loaded = await GetInformationDirectly(); } catch (WebException e) { errors.Add($"Can’t load any information: {GetFailedReason(e)}."); return; } if (UpdateValues(loaded, errors, false) != null) { // Loaded data isn’t for this server (port by which it was loaded differs). // Won’t even set drivers count in this case, whole data is obsviously wrong. return; } driversCount = loaded.Clients; informationUpdated = true; } if (mode != UpdateMode.Lite) { UpdateProgress = new AsyncProgressEntry("Loading actual server information…", 0.2); ServerInformationComplete loaded; try { loaded = await GetInformation(informationUpdated); } catch (WebException e) { errors.Add($"Can’t load information: {GetFailedReason(e)}."); return; } if (loaded != null) { if (loaded.Ip == Ip && loaded.PortHttp == PortHttp || informationUpdated || loaded.LoadedDirectly) { // If loaded information is compatible with existing, use it immediately. Otherwise — apparently, // server changed — we’ll try to load an actual data directly from it later, but only if it wasn’t // loaded just before that and loaded information wasn’t loaded from it. if (UpdateValues(loaded, errors, false) != null) { return; } driversCount = loaded.Clients; } else { ServerInformation directlyLoaded; try { directlyLoaded = await GetInformationDirectly(); } catch (WebException e) { errors.Add($"Can’t load new information: {GetFailedReason(e)}."); return; } if (UpdateValues(directlyLoaded, errors, false) != null) { return; } driversCount = loaded.Clients; } } } if (Ping == null || mode == UpdateMode.Full || !SettingsHolder.Online.PingOnlyOnce) { UpdateProgress = new AsyncProgressEntry("Pinging server…", 0.3); var pair = SettingsHolder.Online.ThreadsPing ? await Task.Run(() => KunosApiProvider.TryToPingServer(Ip, Port, SettingsHolder.Online.PingTimeout)) : await KunosApiProvider.TryToPingServerAsync(Ip, Port, SettingsHolder.Online.PingTimeout); if (pair != null) { Ping = (long)pair.Item2.TotalMilliseconds; } else { Ping = null; errors.Add(ToolsStrings.Online_Server_CannotPing); return; } } UpdateProgress = new AsyncProgressEntry("Loading players list…", 0.4); ServerCarsInformation information; try { information = await KunosApiProvider.GetCarsInformationAsync(Ip, PortHttp); } catch (WebException e) { errors.Add($"Can’t load drivers information: {GetFailedReason(e)}."); return; } if (!BookingMode) { CurrentDriversCount = information.Cars.Count(x => x.IsConnected); driversCount = -1; } var currentDrivers = (BookingMode ? information.Cars : information.Cars.Where(x => x.IsConnected)) .Select(x => { var driver = CurrentDrivers?.FirstOrDefault(y => y.Name == x.DriverName && y.Team == x.DriverTeam && y.CarId == x.CarId && y.CarSkinId == x.CarSkinId) ?? new CurrentDriver(x); return(driver); }) .ToList(); if (CurrentDrivers == null || !CurrentDrivers.SequenceEqual(currentDrivers)) { CurrentDrivers = currentDrivers; var count = 0; var booked = false; foreach (var x in currentDrivers) { if (x.IsConnected) { count++; } if (x.IsBookedForPlayer) { booked = true; SetSelectedCarEntry(Cars?.GetByIdOrDefault(x.CarId, StringComparison.OrdinalIgnoreCase)); } } ConnectedDrivers = count; IsBookedForPlayer = booked; } if (Cars == null) { Logging.Unexpected(); errors.Add("Data is still missing"); return; } for (int i = 0, c = Cars.Count; i < c; i++) { var entry = Cars[i]; var wrapper = entry.CarObjectWrapper; if (wrapper == null) { continue; } /* load car if not loaded */ CarObject car; if (wrapper.IsLoaded) { car = (CarObject)wrapper.Value; } else { UpdateProgress = new AsyncProgressEntry($"Loading cars ({wrapper.Id})…", 0.5 + 0.4 * i / c); await Task.Delay(fast? 10 : 50); car = (CarObject)await wrapper.LoadedAsync(); } /* load skin */ if (!car.SkinsManager.IsLoaded) { UpdateProgress = new AsyncProgressEntry($"Loading {car.DisplayName} skins…", 0.5 + 0.4 * (0.5 + i) / c); await Task.Delay(fast? 10 : 50); await car.SkinsManager.EnsureLoadedAsync(); } /* set next available skin */ if (CurrentSessionType == Game.SessionType.Booking) { entry.AvailableSkin = car.SelectedSkin; entry.Total = 0; entry.Available = 0; entry.IsAvailable = true; } else { var cars = information.Cars.Where(x => x.IsEntryList && string.Equals(x.CarId, entry.Id, StringComparison.OrdinalIgnoreCase)).ToList(); string availableSkinId; if (BookingMode) { availableSkinId = cars.FirstOrDefault(x => x.IsRequestedGuid)?.CarSkinId; entry.Total = 0; entry.Available = 0; entry.IsAvailable = true; } else { availableSkinId = cars.FirstOrDefault(y => !y.IsConnected)?.CarSkinId; entry.Total = cars.Count; entry.Available = cars.Count(y => !y.IsConnected); entry.IsAvailable = entry.Available > 0; } entry.AvailableSkin = availableSkinId == null ? null : availableSkinId == string.Empty ? car.GetFirstSkinOrNull() : car.GetSkinById(availableSkinId); } // TODO: Revert back `errors.Add(ToolsStrings.Online_Server_CarsDoNotMatch);` (?) } if (IsBookedForPlayer) { FixedCar = true; } else { FixedCar = false; LoadSelectedCar(); } } catch (InformativeException e) { errors.Add($@"{e.Message}."); } catch (Exception e) { errors.Add(string.Format(ToolsStrings.Online_Server_UnhandledError, e.Message)); Logging.Error(e); } finally { if (driversCount != -1) { CurrentDriversCount = driversCount; } UpdateProgress = AsyncProgressEntry.Ready; Errors = errors; Status = errors.Any() ? ServerStatus.Error : ServerStatus.Ready; AvailableUpdate(); _updating = false; } }