mirror of
https://github.com/MCCTeam/Minecraft-Console-Client
synced 2025-10-14 21:22:49 +00:00
Use FileMonitor to synchronize Mailer bot (#1108) (v2)
Move SessionFileMonitor into a generic FileMonitor class Use FileMonintor for both SessionCache and the Mailer bot Allows multiple MCC instances to share the same database files (Add files missing in the previous commit)
This commit is contained in:
parent
5028cce2a5
commit
ce83cc0a33
6 changed files with 215 additions and 85 deletions
|
|
@ -1,27 +1,32 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
|
||||
namespace MinecraftClient.Protocol.Session
|
||||
namespace MinecraftClient
|
||||
{
|
||||
/// <summary>
|
||||
/// Monitor session file changes on disk
|
||||
/// Monitor file changes on disk
|
||||
/// </summary>
|
||||
class SessionFileMonitor
|
||||
public class FileMonitor : IDisposable
|
||||
{
|
||||
private FileSystemWatcher monitor;
|
||||
private Thread polling;
|
||||
private FileSystemWatcher monitor = null;
|
||||
private Thread polling = null;
|
||||
|
||||
/// <summary>
|
||||
/// Create a new SessionFileMonitor and start monitoring
|
||||
/// Create a new FileMonitor and start monitoring
|
||||
/// </summary>
|
||||
/// <param name="folder">Folder to monitor</param>
|
||||
/// <param name="filename">Filename inside folder</param>
|
||||
/// <param name="handler">Callback for file changes</param>
|
||||
public SessionFileMonitor(string folder, string filename, FileSystemEventHandler handler)
|
||||
public FileMonitor(string folder, string filename, FileSystemEventHandler handler)
|
||||
{
|
||||
if (Settings.DebugMessages)
|
||||
ConsoleIO.WriteLineFormatted("§8Initializing disk session cache using FileSystemWatcher");
|
||||
{
|
||||
string callerClass = new System.Diagnostics.StackFrame(1).GetMethod().DeclaringType.Name;
|
||||
ConsoleIO.WriteLineFormatted(String.Format("§8[{0}] Initializing FileSystemWatcher for file: {1}", callerClass, Path.Combine(folder, filename)));
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
|
|
@ -36,13 +41,29 @@ namespace MinecraftClient.Protocol.Session
|
|||
catch
|
||||
{
|
||||
if (Settings.DebugMessages)
|
||||
ConsoleIO.WriteLineFormatted("§8Failed to initialize FileSystemWatcher, retrying using Polling");
|
||||
{
|
||||
string callerClass = new System.Diagnostics.StackFrame(1).GetMethod().DeclaringType.Name;
|
||||
ConsoleIO.WriteLineFormatted(String.Format("§8[{0}] Failed to initialize FileSystemWatcher, retrying using Polling", callerClass));
|
||||
}
|
||||
|
||||
monitor = null;
|
||||
polling = new Thread(() => PollingThread(folder, filename, handler));
|
||||
polling.Name = String.Format("{0} Polling thread: {1}", this.GetType().Name, Path.Combine(folder, filename));
|
||||
polling.Start();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Stop monitoring and dispose the inner resources
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
if (monitor != null)
|
||||
monitor.Dispose();
|
||||
if (polling != null)
|
||||
polling.Abort();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fallback polling thread for use when operating system does not support FileSystemWatcher
|
||||
/// </summary>
|
||||
|
|
@ -51,7 +72,7 @@ namespace MinecraftClient.Protocol.Session
|
|||
/// <param name="handler">Callback when file changes</param>
|
||||
private void PollingThread(string folder, string filename, FileSystemEventHandler handler)
|
||||
{
|
||||
string filePath = String.Concat(folder, Path.DirectorySeparatorChar, filename);
|
||||
string filePath = Path.Combine(folder, filename);
|
||||
DateTime lastWrite = GetLastWrite(filePath);
|
||||
while (true)
|
||||
{
|
||||
|
|
@ -79,5 +100,62 @@ namespace MinecraftClient.Protocol.Session
|
|||
}
|
||||
else return DateTime.MinValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens a text file, reads all lines of the file, and then closes the file. Retry several times if the file is in use
|
||||
/// </summary>
|
||||
/// <param name="filePath">The file to open for reading</param>
|
||||
/// <param name="maxTries">Maximum read attempts</param>
|
||||
/// <param name="encoding">Encoding (default is UTF8)</param>
|
||||
/// <exception cref="System.IO.IOException">Thrown when failing to read the file despite multiple retries</exception>
|
||||
/// <returns>All lines</returns>
|
||||
public static string[] ReadAllLinesWithRetries(string filePath, int maxTries = 3, Encoding encoding = null)
|
||||
{
|
||||
int attempt = 0;
|
||||
if (encoding == null)
|
||||
encoding = Encoding.UTF8;
|
||||
while (true)
|
||||
{
|
||||
try
|
||||
{
|
||||
return File.ReadAllLines(filePath, encoding);
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
attempt++;
|
||||
if (attempt < maxTries)
|
||||
Thread.Sleep(new Random().Next(50, 100) * attempt); // Back-off like CSMA/CD
|
||||
else throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new file, writes a collection of strings to the file, and then closes the file.
|
||||
/// </summary>
|
||||
/// <param name="filePath">The file to open for writing</param>
|
||||
/// <param name="lines">The lines to write to the file</param>
|
||||
/// <param name="maxTries">Maximum read attempts</param>
|
||||
/// <param name="encoding">Encoding (default is UTF8)</param>
|
||||
public static void WriteAllLinesWithRetries(string filePath, IEnumerable<string> lines, int maxTries = 3, Encoding encoding = null)
|
||||
{
|
||||
int attempt = 0;
|
||||
if (encoding == null)
|
||||
encoding = Encoding.UTF8;
|
||||
while (true)
|
||||
{
|
||||
try
|
||||
{
|
||||
File.WriteAllLines(filePath, lines, encoding);
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
attempt++;
|
||||
if (attempt < maxTries)
|
||||
Thread.Sleep(new Random().Next(50, 100) * attempt); // Back-off like CSMA/CD
|
||||
else throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue