From 2ebc8eded5bb646c65dc3b2fa2470ee76b42f798 Mon Sep 17 00:00:00 2001 From: ORelio Date: Wed, 17 Apr 2019 05:18:19 +0200 Subject: [PATCH] Implement file polling for disk session cache For use in case FileSystemWatcher does not work, see #684 --- MinecraftClient/MinecraftClient.csproj | 1 + .../Protocol/Session/SessionCache.cs | 11 +-- .../Protocol/Session/SessionFileMonitor.cs | 83 +++++++++++++++++++ 3 files changed, 86 insertions(+), 9 deletions(-) create mode 100644 MinecraftClient/Protocol/Session/SessionFileMonitor.cs diff --git a/MinecraftClient/MinecraftClient.csproj b/MinecraftClient/MinecraftClient.csproj index adddde88..4eb072df 100644 --- a/MinecraftClient/MinecraftClient.csproj +++ b/MinecraftClient/MinecraftClient.csproj @@ -97,6 +97,7 @@ + diff --git a/MinecraftClient/Protocol/Session/SessionCache.cs b/MinecraftClient/Protocol/Session/SessionCache.cs index b388018c..279e4375 100644 --- a/MinecraftClient/Protocol/Session/SessionCache.cs +++ b/MinecraftClient/Protocol/Session/SessionCache.cs @@ -23,8 +23,8 @@ namespace MinecraftClient.Protocol.Session "launcher_profiles.json" ); + private static SessionFileMonitor cachemonitor; private static Dictionary sessions = new Dictionary(); - private static FileSystemWatcher cachemonitor = new FileSystemWatcher(); private static Timer updatetimer = new Timer(100); private static List> pendingadds = new List>(); private static BinaryFormatter formatter = new BinaryFormatter(); @@ -81,15 +81,8 @@ namespace MinecraftClient.Protocol.Session /// TRUE if session tokens are seeded from file public static bool InitializeDiskCache() { - cachemonitor.Path = AppDomain.CurrentDomain.BaseDirectory; - cachemonitor.IncludeSubdirectories = false; - cachemonitor.Filter = SessionCacheFilePlaintext; - cachemonitor.NotifyFilter = NotifyFilters.LastWrite; - cachemonitor.Changed += new FileSystemEventHandler(OnChanged); - cachemonitor.EnableRaisingEvents = true; - + cachemonitor = new SessionFileMonitor(AppDomain.CurrentDomain.BaseDirectory, SessionCacheFilePlaintext, new FileSystemEventHandler(OnChanged)); updatetimer.Elapsed += HandlePending; - return LoadFromDisk(); } diff --git a/MinecraftClient/Protocol/Session/SessionFileMonitor.cs b/MinecraftClient/Protocol/Session/SessionFileMonitor.cs new file mode 100644 index 00000000..00cedf77 --- /dev/null +++ b/MinecraftClient/Protocol/Session/SessionFileMonitor.cs @@ -0,0 +1,83 @@ +using System; +using System.IO; +using System.Threading; + +namespace MinecraftClient.Protocol.Session +{ + /// + /// Monitor session file changes on disk + /// + class SessionFileMonitor + { + private FileSystemWatcher monitor; + private Thread polling; + + /// + /// Create a new SessionFileMonitor and start monitoring + /// + /// Folder to monitor + /// Filename inside folder + /// Callback for file changes + public SessionFileMonitor(string folder, string filename, FileSystemEventHandler handler) + { + if (Settings.DebugMessages) + ConsoleIO.WriteLineFormatted("§8Initializing disk session cache using FileSystemWatcher"); + + try + { + monitor = new FileSystemWatcher(); + monitor.Path = folder; + monitor.IncludeSubdirectories = false; + monitor.Filter = filename; + monitor.NotifyFilter = NotifyFilters.LastWrite; + monitor.Changed += handler; + monitor.EnableRaisingEvents = true; + } + catch + { + if (Settings.DebugMessages) + ConsoleIO.WriteLineFormatted("§8Failed to initialize FileSystemWatcher, retrying using Polling"); + + polling = new Thread(() => PollingThread(folder, filename, handler)); + polling.Start(); + } + } + + /// + /// Fallback polling thread for use when operating system does not support FileSystemWatcher + /// + /// Folder to monitor + /// File name to monitor + /// Callback when file changes + private void PollingThread(string folder, string filename, FileSystemEventHandler handler) + { + string filePath = String.Concat(folder, Path.DirectorySeparatorChar, filename); + DateTime lastWrite = GetLastWrite(filePath); + while (true) + { + Thread.Sleep(5000); + DateTime lastWriteNew = GetLastWrite(filePath); + if (lastWriteNew != lastWrite) + { + lastWrite = lastWriteNew; + handler(this, new FileSystemEventArgs(WatcherChangeTypes.Changed, folder, filename)); + } + } + } + + /// + /// Get last write for a given file + /// + /// File path to get last write from + /// Last write time, or DateTime.MinValue if the file does not exist + private DateTime GetLastWrite(string path) + { + FileInfo fileInfo = new FileInfo(path); + if (fileInfo.Exists) + { + return fileInfo.LastWriteTime; + } + else return DateTime.MinValue; + } + } +}