public static void FileSynchronization(string modsDir, ModelModsFiles serverFiles) { restoreFolderTree(modsDir, serverFiles.FoldersTree); foreach (var serverFile in serverFiles.Files) { var fullName = Path.Combine(modsDir, serverFile.FileName); // Имя присутствует в списке, файл необходимо будет удалить ( или заменить) if (File.Exists(fullName)) { File.Delete(fullName); } if (serverFile.Hash == null) { continue; } // Create the file, or overwrite if the file must exist. using (FileStream fs = File.Create(fullName)) { Loger.Log("Restore: " + fullName); if (serverFile.Hash.Length > 0) { fs.Write(serverFile.Hash, 0, serverFile.Hash.Length); } } } }
private ModelModsFiles checkFiles(ModelModsFiles packet, ServiceContext context) { var NoApproveWorld = ServerManager.FileHashChecker.ApproveWorldType[packet.FolderType]; var filesDir = ServerManager.FileHashChecker.CheckedDirAndFiles[packet.FolderType].ServerDirectory; var foldersTree = ServerManager.FileHashChecker.CheckedDirAndFiles[packet.FolderType].FolderTree; var workDict = ServerManager.FileHashChecker.CheckedDirAndFiles[packet.FolderType].HashFiles; var ignoredFiles = ServerManager.FileHashChecker.CheckedDirAndFiles[packet.FolderType].IgrnoredFiles; var result = new List <ModelFileInfo>(); var allServerFiles = new HashSet <string>(workDict.Keys); var packetFiles = packet.Files != null ? packet.Files : new List <ModelFileInfo>(0); long packetSize = 0; long totalSize = 0; foreach (var modelFile in packetFiles) { var modelFileFileName = modelFile.FileName.ToLower(); if (FileHashChecker.FileNameContainsIgnored(modelFileFileName, ignoredFiles)) { continue; } if (workDict.TryGetValue(modelFileFileName, out ModelFileInfo fileInfo)) { allServerFiles.Remove(modelFileFileName); // if (!ModelFileInfo.UnsafeByteArraysEquale(modelFile.Hash, fileInfo.Hash)) { // read file for send to Client // файл найден, но хеши не совпадают, необходимо заменить файл if (packetSize < MaxPacketSize) { var addFile = GetFile(filesDir, fileInfo.FileName); result.Add(addFile); packetSize += addFile.Size; totalSize += addFile.Size; //Loger.Log($"packetSize={packetSize} totalSize={totalSize}"); } else { var size = GetFileSize(filesDir, fileInfo.FileName); totalSize += size; } } } else { // mark file for delete // Если файл с таким именем не найден, помечаем файл на удаление modelFile.Hash = null; result.Add(modelFile); } } lock (context.Player) { // проверяем в обратном порядке: что бы у клиенты были все файлы if (allServerFiles.Any()) { context.Player.ApproveLoadWorldReason = context.Player.ApproveLoadWorldReason | ApproveLoadWorldReason.NotAllFilesOnClient; foreach (var fileName in allServerFiles) { if (packetSize < MaxPacketSize) { var addFile = GetFile(filesDir, workDict[fileName].FileName); //workDict[fileName].FileName вместо fileName для восстановления заглавных result.Add(addFile); packetSize += addFile.Size; totalSize += addFile.Size; //Loger.Log($"packetSize={packetSize} totalSize={totalSize}"); } else { var size = GetFileSize(filesDir, workDict[fileName].FileName); totalSize += size; } } } // Если файлы не прошли проверку, помечаем флагом, запрет загрузки мира if (result.Any()) { context.Player.ApproveLoadWorldReason = context.Player.ApproveLoadWorldReason | NoApproveWorld; } } if (!result.Any()) { // микроптимизация: если файлы не будут восстанавливаться, не отправляем обратно список папок // на восстановление ( десериализацию папок также тратится время) foldersTree = new FoldersTree(); } return(new ModelModsFiles() { Files = result, FolderType = packet.FolderType, FoldersTree = foldersTree, TotalSize = totalSize, }); }
private ModelModsFiles checkFiles(ModelModsFiles packet, ServiceContext context) { var modsDir = packet.IsSteam ? ServerManager.ServerSettings.SteamWorkShopModsDir : ServerManager.ServerSettings.ModsDirectory; var NoApproveWorld = packet.IsSteam ? ApproveLoadWorldReason.ModsSteamWorkShopFail : ApproveLoadWorldReason.ModsFilesFail; var workDict = packet.IsSteam ? ServerManager.SteamFilesDict : ServerManager.ModFilesDict; var foldersTree = packet.IsSteam ? ServerManager.ServerSettings.SteamDirConfig.FoldersTree : ServerManager.ServerSettings.ModsDirConfig.FoldersTree; var result = new List <ModelFileInfo>(); var allServerFiles = new HashSet <string>(workDict.Keys); foreach (var file in packet.Files) { if (workDict.TryGetValue(file.FileName, out ModelFileInfo fileInfo)) { allServerFiles.Remove(file.FileName); // if (!ModelFileInfo.UnsafeByteArraysEquale(file.Hash, fileInfo.Hash)) { // read file for send to Client // файл найден, но хеши не совпадают, необходимо заменить файл result.Add(GetFile(modsDir, fileInfo.FileName)); } } else { // mark file for delete // Если файл с таким именем не найден, помечаем файл на удаление file.Hash = null; result.Add(file); } } lock (context.Player) { // проверяем в обратном порядке: что бы у клиенты были все файлы if (allServerFiles.Any()) { context.Player.ApproveLoadWorldReason = context.Player.ApproveLoadWorldReason | ApproveLoadWorldReason.NotAllFilesOnClient; foreach (var fileName in allServerFiles) { result.Add(GetFile(modsDir, fileName)); } } // Если файлы не прошли проверку, помечаем флагом, запрет загрузки мира if (result.Any()) { context.Player.ApproveLoadWorldReason = context.Player.ApproveLoadWorldReason | NoApproveWorld; } } if (!result.Any()) { // микроптимизация: если файлы не будут восстанавливаться, не отправляем обратно список папок // на восстановление ( десериализацию папок также тратится время) foldersTree = new FoldersTree(); } return(new ModelModsFiles() { Files = result, IsSteam = packet.IsSteam, FoldersTree = foldersTree }); }
/// <summary> /// Генерируем запрос серверу в зависимости от контекста /// </summary> /// <param name="context"></param> /// <returns></returns> public ApproveLoadWorldReason GenerateRequestAndDoJob(object context) { // each request - response Client-Server-Client ~100-200 ms, get all check hash in one request // каждый запрос-отклик к серверу ~100-200 мс, получаем за один запрос все файлы // ~40 000 files *512 SHA key ~ size of package ~ 2,5 Mb ApproveLoadWorldReason result = ApproveLoadWorldReason.LoginOk; bool downloading = true; long totalSize = 0; long downloadSize = 0; while (downloading) { var clientFileChecker = (ClientFileChecker)context; var model = new ModelModsFiles() { Files = clientFileChecker.FilesHash, FolderType = clientFileChecker.FolderType, }; UpdateModsWindow.Title = "OC_Hash_Downloading".Translate(); UpdateModsWindow.HashStatus = ""; UpdateModsWindow.SummaryList = null; Loger.Log($"Send hash {clientFileChecker.Folder}"); var res = _sessionClient.TransObject2 <ModelModsFiles>(model, RequestTypePackage, ResponseTypePackage); if (res.Files.Count > 0) { if (totalSize == 0) { totalSize = res.TotalSize; } downloadSize += res.Files.Sum(f => f.Size); Loger.Log($"Files that need for a change: {downloadSize}/{totalSize} count={res.Files.Count}"); var pr = downloadSize > totalSize || totalSize == 0 ? 100 : downloadSize * 100 / totalSize; UpdateModsWindow.HashStatus = "OC_Hash_Downloading_Finish".Translate() + pr.ToString() + "%"; result = result | ApproveLoadWorldReason.ModsFilesFail; //Loger.Log("TTTTT 0"); FileChecker.FileSynchronization(clientFileChecker.Folder, res); //Loger.Log("TTTTT 1"); clientFileChecker.RecalculateHash(res.Files.Select(f => f.FileName).ToList()); //Loger.Log("TTTTT 2"); var addList = res.Files .Select(f => f.FileName) .Where(f => f.Contains("\\")) .Select(f => f.Substring(0, f.IndexOf("\\"))) //.Distinct() //вместо дистинкта группируем без разницы заглавных букв, но сохраняем оригинальное название .Select(f => new { orig = f, comp = f.ToLower() }) .GroupBy(p => p.comp) .Select(g => g.Max(p => p.orig)) .Where(f => UpdateModsWindow.SummaryList == null || !UpdateModsWindow.SummaryList.Any(sl => sl == f)) .ToList(); if (UpdateModsWindow.SummaryList == null) { UpdateModsWindow.SummaryList = addList; } else { UpdateModsWindow.SummaryList.AddRange(addList); } } downloading = res.TotalSize != 0; } return(result); }