using AnotherReplayReader.ReplayFile; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; using System.Windows; namespace AnotherReplayReader { public static class ReplayAutoSaver { private static int _errorMessageCount = 0; public static void SpawnAutoSaveReplaysTask(string replayFolderPath) { Task.Run(() => AutoSaveReplays(replayFolderPath)); } private static async Task AutoSaveReplays(string replayFolderPath) { const string ourPrefix = "自动保存"; // filename and last write time var previousFiles = new Dictionary(StringComparer.OrdinalIgnoreCase); // filename and file size var lastReplays = new Dictionary(StringComparer.OrdinalIgnoreCase); while (true) { try { var changed = (from fileName in Directory.GetFiles(replayFolderPath, "*.RA3Replay") let info = new FileInfo(fileName) where !info.Name.StartsWith(ourPrefix) where !previousFiles.ContainsKey(info.FullName) || previousFiles[info.FullName] != info.LastWriteTimeUtc select info).ToList(); foreach (var info in changed) { previousFiles[info.FullName] = info.LastWriteTimeUtc; } var replays = changed.Select(info => { Debug.Instance.DebugMessage += $"正在尝试检测已更改的文件:{info.FullName}\r\n"; try { using var stream = info.Open(FileMode.Open, FileAccess.Read, FileShare.ReadWrite); return new Replay(info.FullName, stream); } catch (Exception e) { Debug.Instance.DebugMessage += $"自动保存录像/检测录像更改时发生错误:{e}\r\n"; return null; } }).Where(replay => replay != null); var newLastReplays = from replay in replays let threshold = Math.Abs((DateTime.UtcNow - replay.Date).TotalSeconds) let endDate = replay.Date.Add(replay.Length ?? TimeSpan.Zero) let endThreshold = Math.Abs((DateTime.UtcNow - endDate).TotalSeconds) where threshold < 40 || endThreshold < 40 select replay; var toBeChecked = newLastReplays.ToDictionary(replay => replay.Path, StringComparer.OrdinalIgnoreCase); foreach (var savedLastReplay in lastReplays.Keys) { if (!toBeChecked.ContainsKey(savedLastReplay)) { try { using var stream = File.Open(savedLastReplay, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); toBeChecked.Add(savedLastReplay, new Replay(savedLastReplay, stream)); } catch (Exception e) { Debug.Instance.DebugMessage += $"自动保存录像/检测录像更改时发生错误:{e}\r\n"; } } } foreach (var kv in toBeChecked) { Debug.Instance.DebugMessage += $"正在检测录像更改:{kv.Key}\r\n"; var replay = kv.Value; if (lastReplays.TryGetValue(kv.Key, out var fileSize)) { if (fileSize == replay.Size) { // skip if size is not changed Debug.Instance.DebugMessage += $"已跳过未更改的录像:{kv.Key}\r\n"; continue; } } Debug.Instance.DebugMessage += $"将会自动保存已更改的录像:{kv.Key}\r\n"; lastReplays[kv.Key] = replay.Size; var date = replay.Date; var playerString = $"{replay.NumberOfPlayingPlayers}名玩家"; if (replay.NumberOfPlayingPlayers <= 2) { var playingPlayers = from player in replay.Players let faction = ModData.GetFaction(replay.Mod, player.FactionId) where faction.Kind != FactionKind.Observer select $"{player.PlayerName}({faction.Name})"; playerString = playingPlayers.Aggregate(string.Empty, (x, y) => x + y); } var dateString = $"{date.Year}{date.Month:D2}{date.Day:D2}_{date.Hour:D2}{date.Minute:D2}{date.Second:D2}"; var destinationPath = Path.Combine(replayFolderPath, $"{ourPrefix}-{playerString}{dateString}.RA3Replay"); try { File.Copy(replay.Path, destinationPath, true); } catch (Exception e) { throw new Exception($"复制文件({replay.Path} -> {destinationPath})失败:{e.Message}", e); } } } catch (Exception e) { var errorString = $"自动保存录像时出现错误:\r\n{e}\r\n"; Debug.Instance.DebugMessage += errorString; if (Interlocked.Increment(ref _errorMessageCount) == 1) { _ = Application.Current.Dispatcher.InvokeAsync(() => { try { MessageBox.Show(errorString); } finally { Interlocked.Decrement(ref _errorMessageCount); } }); } } await Task.Delay(10 * 1000).ConfigureAwait(false); } } } }