/// <summary> /// Asynchronously download a map. /// </summary> /// <param name="map">The map to download</param> /// <param name="url">URL of the online map ZIP</param> /// <param name="filePath">Path to local file name</param> /// <param name="displayName">Friendly display name</param> /// <returns>DownloadResult detailing outcome of the download</returns> internal Task <DownloadResult> DownloadMapFile(OverloadMap map, Uri url, string filePath, string displayName) { DownloadResult result = new DownloadResult(); var tcs = new TaskCompletionSource <DownloadResult>(); Task.Run(async() => { bool hasProgresChanged = false; var timer = new System.Timers.Timer(new TimeSpan(0, 0, DownloadTimeoutSeconds).TotalMilliseconds); var client = new WebClient(); void downloadHandler(object s, DownloadProgressChangedEventArgs e) => hasProgresChanged = true; void timerHandler(object s, ElapsedEventArgs e) { timer.Stop(); if (hasProgresChanged) { timer.Start(); hasProgresChanged = false; } else { result.Message = String.Format($"Timeout downloading {displayName}"); CleanUp(); tcs.TrySetException(new TimeoutException("Download timed out")); } } void CleanUp() { client.DownloadProgressChanged -= downloadHandler; client.Dispose(); timer.Elapsed -= timerHandler; timer.Dispose(); } try { client.DownloadProgressChanged += downloadHandler; timer.Elapsed += timerHandler; timer.Start(); //LogMessage(String.Format($"Downloading map {displayName}.")); await client.DownloadFileTaskAsync(url, filePath); LogMessage(String.Format($"{displayName} download complete.")); if (!filePath.Contains(Path.DirectorySeparatorChar + "DLC" + Path.DirectorySeparatorChar)) { map.LocalZipFileName = filePath; } else { map.LocalDLCZipFileName = filePath; } map.ZipName = Path.GetFileName(filePath); // Set local files date and time. File.SetCreationTime(filePath, map.DateTime); File.SetLastWriteTime(filePath, map.DateTime); result.Succes = true; } catch (Exception ex) { result.Message = String.Format($"Error downloading {displayName}: {ex.Message}"); tcs.TrySetException(ex); } finally { CleanUp(); } return(tcs.TrySetResult(result)); }); return(tcs.Task); }
/// <summary> /// Updates both online maps available for download as well as local maps. /// </summary> /// <param name="mapListUrl">URL to online JSON master map list.</param> /// <param name="dlcMaps">Path to local DLC folder (or null).</param> /// <param name="applicationMaps">Path to local application folder (or null).</param> public void UpdateMapList(string mapListUrl = null, string dlcMaps = null, string applicationMaps = null) { // Check for override of default URL to JSON map list. if (String.IsNullOrEmpty(mapListUrl)) { mapListUrl = DefaultOnlineMapListUrl; } // Check DLC/application directory names. if (!String.IsNullOrEmpty(dlcMaps)) { dlcMapFolder = dlcMaps; if (!OverloadServerToolApplication.ValidDirectoryName(dlcMapFolder)) { LogErrorMessage("DLC directory name is invalid!"); return; } } if (!String.IsNullOrEmpty(applicationMaps)) { applicationMapFolder = applicationMaps; if (!OverloadServerToolApplication.ValidDirectoryName(applicationMapFolder)) { LogErrorMessage("Application data directory name is invalid!"); return; } } // Need at least one defined folder to store maps. if (String.IsNullOrEmpty(applicationMapFolder) && String.IsNullOrEmpty(dlcMapFolder)) { LogErrorMessage("No application data directory or DLC directory found!"); return; } // Collect all new online and local maps. SortedList <string, OverloadMap> newMapList = new SortedList <String, OverloadMap>(); // First find all online maps. try { // Get URL base so we can construct full URLs to the online map ZIP files. Uri uri = new Uri(mapListUrl); string baseUrl = String.Concat(uri.Scheme, Uri.SchemeDelimiter, uri.Host); // Get map list and build internal map master list. string jsonMapList = new WebClient().DownloadString(mapListUrl); dynamic mapList = JsonConvert.DeserializeObject(jsonMapList); foreach (var map in mapList) { // Check to make sure it is a ZIP file before adding it to the list. string mapUrl = baseUrl + map.url; Uri mapUri = new Uri(mapUrl); if (mapUri.Segments.Length > 2) { // Get the ZIP file name from URL. string mapZipName = mapUri.Segments[mapUri.Segments.Length - 1]; if (mapZipName.ToLower().EndsWith(".zip")) { // Uppercase first char in map name. mapZipName = mapZipName.Substring(0, 1).ToUpper() + mapZipName.Substring(1); OverloadMap newMap = new OverloadMap(baseUrl + map.url, UnixTimeStampToDateTime(Convert.ToDouble(map.mtime)), Convert.ToInt32(map.size), mapZipName); newMapList.Add(mapZipName.ToLower(), newMap); } } } } catch (Exception ex) { LogErrorMessage(String.Format($"Unable to get online map list: {ex.Message}")); } // Now add any local maps or update info if existing local map file exists. if (OverloadServerToolApplication.ValidDirectoryName(applicationMapFolder)) { try { Directory.CreateDirectory(applicationMapFolder); string[] list = Directory.GetFiles(applicationMapFolder, "*.zip*"); foreach (string mapFileName in list) { if (mapFileName.EndsWith(HiddenMarker) || mapFileName.ToLower().EndsWith(".zip")) { string mapKey = Path.GetFileName(mapFileName).ToLower(); FileInfo fiLocalMap = new FileInfo(mapFileName); OverloadMap newMap = new OverloadMap(null, UnixTimeStampToDateTime(0), Convert.ToInt32(fiLocalMap.Length), mapFileName); bool found = false; foreach (KeyValuePair <string, OverloadMap> map in newMapList) { if (newMap.SameZipFileName(map.Value)) { // We found a local map that matches the ZIP filename as found online. found = true; // Uppercase first char in map name and remove hidden marker if present. string mapZipName = fiLocalMap.Name; mapZipName = mapZipName.Substring(0, 1).ToUpper() + mapZipName.Substring(1); mapZipName = mapZipName.Replace(HiddenMarker, ""); map.Value.ZipName = mapZipName; // Save full path to ZIP file in the application data folder. map.Value.LocalZipFileName = fiLocalMap.FullName; } } if (!found) { newMapList.Add(mapFileName.ToLower(), newMap); } } } } catch (Exception ex) { LogErrorMessage(String.Format($"Unable to get local maps: {ex.Message}")); } } if (OverloadServerToolApplication.ValidDirectoryName(dlcMapFolder)) { try { Directory.CreateDirectory(dlcMapFolder); string[] list = Directory.GetFiles(dlcMapFolder, "*.zip*"); foreach (string mapFileName in list) { if (mapFileName.EndsWith(HiddenMarker) || mapFileName.ToLower().EndsWith(".zip")) { string mapKey = Path.GetFileName(mapFileName).ToLower(); FileInfo fiLocalMap = new FileInfo(mapFileName); OverloadMap newMap = new OverloadMap(null, UnixTimeStampToDateTime(0), Convert.ToInt32(fiLocalMap.Length), mapFileName); bool found = false; foreach (KeyValuePair <string, OverloadMap> map in newMapList) { if (newMap.SameZipFileName(map.Value)) { // We found a local map that matches the ZIP filename as found online. found = true; // Uppercase first char in map name and remove hidden marker if present. string mapZipName = fiLocalMap.Name; mapZipName = mapZipName.Substring(0, 1).ToUpper() + mapZipName.Substring(1); mapZipName = mapZipName.Replace(HiddenMarker, ""); map.Value.ZipName = mapZipName; // Save full path to ZIP file in the DLC folder. map.Value.LocalDLCZipFileName = fiLocalMap.FullName; } } if (!found) { newMapList.Add(mapFileName.ToLower(), newMap); } } } } catch (Exception ex) { LogErrorMessage(String.Format($"Unable to get local maps: {ex.Message}")); } } // Update maps. Maps = newMapList; }
/// <summary> /// Update a single Overload map. /// </summary> /// <param name="map">The map to update.</param> /// <returns></returns> public async Task <bool> UpdateMap(OverloadMap map, bool forceUpdate) { Checked++; if (String.IsNullOrEmpty(map.Url)) { return(false); } string mapZipName = map.ZipName; string mapDirectoryPath = (String.IsNullOrEmpty(dlcMapFolder) || !SaveNewMapsToDLCFolder) ? applicationMapFolder : dlcMapFolder; string mapZipFilePath = WebUtility.UrlDecode(Path.Combine(mapDirectoryPath, mapZipName)); string mapZipDisplayName = WebUtility.UrlDecode(mapZipName).Trim(); // Check if we should use existing download path. if (map.InDLCFolder) { mapDirectoryPath = Path.GetDirectoryName(map.LocalDLCZipFileName); mapZipFilePath = map.LocalDLCZipFileName; } else if (map.InApplicationDataFolder) { mapDirectoryPath = Path.GetDirectoryName(map.LocalZipFileName); mapZipFilePath = map.LocalZipFileName; } // Create (DLC or application data) directory if it doesn't exist. if (!Directory.Exists(mapDirectoryPath)) { Directory.CreateDirectory(mapDirectoryPath); } // Don't update hidden maps. if (File.Exists(mapZipFilePath + HiddenMarker)) { return(false); } // Check for new map file. if (!forceUpdate && OverloadServerToolApplication.ValidFileName(mapZipFilePath)) { if (File.Exists(mapZipFilePath)) { // Map already downloaded. Compare date and size against online version. FileInfo fi = new FileInfo(mapZipFilePath); if ((fi.Length == map.Size) && (fi.LastWriteTime == map.DateTime)) { return(false); } } else { // Only update existing maps? if (OnlyUpdateExistingMaps) { return(false); } } } try { bool existingFile = File.Exists(mapZipFilePath); // Download map. DownloadResult result = await DownloadMapFile(map, new Uri(map.Url), mapZipFilePath, mapZipDisplayName); if (result.Exception != null) { throw result.Exception; } if (result.Succes == false) { if (String.IsNullOrEmpty(result.Message)) { result.Message = String.Format($"Unable to download {mapZipDisplayName}"); } try { File.Delete(mapZipFilePath); } catch { } throw new Exception(String.Format($"Unable to download {result.Message }")); } // LogMessage(String.Format($"Downloading map {mapZipDisplayName}.")); if (existingFile) { Updated++; } else { Created++; } return(true); } catch (Exception ex) { Errors++; LogErrorMessage(String.Format($"Error downloading {mapZipDisplayName}: {ex.Message}")); return(false); } }
/// <summary> /// Returns TRUE if this map has the same ZIP map filename as 'compareToMap' /// Doesn't matter if either map is hidden or not. /// </summary> /// <param name="compareZipName"></param> /// <returns></returns> public bool SameZipFileName(OverloadMap compareToMap) { return(compareToMap.ZipName.Replace(MapHiddenMarker, "").ToLower() == this.ZipName.ToLower().Replace(MapHiddenMarker, "")); }