diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 858c91f..3178665 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -3,7 +3,6 @@ name: CI # Controls when the action will run. Triggers the workflow on push or pull request -# events but only for the main branch on: push: branches: [ main, dev ] diff --git a/.gitmodules b/.gitmodules index b6c46cb..a013ccf 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,3 +7,6 @@ [submodule "Base32"] path = Base32 url = https://gist.github.com/9335c394c5cc65404c4cf9aceab04143.git +[submodule "JsonKit"] + path = JsonKit + url = https://github.com/toptensoftware/JsonKit.git diff --git a/DiffBackup/Backup/BackupService.cs b/DiffBackup/Backup/BackupService.cs index 66c64b0..f87fa27 100644 --- a/DiffBackup/Backup/BackupService.cs +++ b/DiffBackup/Backup/BackupService.cs @@ -5,8 +5,10 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using deltaq_tie.BsDiff; +using deltaq_tie_tdiff.BsDiff; +using DiffBackup.Backup.Config; using DiffBackup.Logger; +using Terraria; using TShockAPI.Extensions; #nullable enable @@ -15,14 +17,19 @@ namespace DiffBackup.Backup { public class BackupService : IDisposable, IBackupService { + private readonly CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource(); private readonly SemaphoreSlim _saveDiffSemaphore = new SemaphoreSlim(1, 1); + public readonly AllConfig Config; public readonly ITlog Log; private Stopwatch _saveDiffStopWatch = new Stopwatch(); + private readonly Stopwatch _scheduledCleanupStopWatch = Stopwatch.StartNew(); - public BackupService(ITlog log, IBackupStrategy? strategy = null, BackupIOWorker? worker = null) + public BackupService(ITlog log, AllConfig config, IBackupStrategy? strategy = null, + BackupIOWorker? worker = null) { Log = log; - Strategy = strategy ?? new DefaultBackupStrategy(Log); + Config = config.Clone(); + Strategy = strategy ?? new DefaultBackupStrategy(Log, config.Strategy); Worker = worker ?? new BackupIOWorker(log); var env = Environment.GetEnvironmentVariables(); @@ -35,9 +42,16 @@ public BackupService(ITlog log, IBackupStrategy? strategy = null, BackupIOWorker TimeSpan.FromSeconds( double.Parse(Environment.GetEnvironmentVariable(TdiffOverwriteThrottleTimeSpan))); } + + Task.Factory.StartNew(CleanupScheduleLoop, _cancellationTokenSource.Token, + TaskCreationOptions.LongRunning | TaskCreationOptions.DenyChildAttach, TaskScheduler.Default); } - public TimeSpan ThrottleTimeSpan { get; set; } = TimeSpan.FromMinutes(1); + public TimeSpan ThrottleTimeSpan + { + get => Config.Internal.ThrottleTimeSpan; + set => Config.Internal.ThrottleTimeSpan = value; + } public bool ShouldThrottle => ThrottleTimeSpan > TimeSpan.Zero && _saveDiffStopWatch.IsRunning && @@ -57,26 +71,30 @@ public async Task StartBackup(string path, DateTime? dateTime = null, public async Task> StartCleanup(string worldPath, CancellationToken cancellationToken = default) { + using var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, + _cancellationTokenSource.Token); var repo = new BackupRepository(BackupUtils.GetRepoFilePath(Path.GetFullPath(worldPath))); - return await Worker.IoScheduleFunction(() => Cleanup(repo), cancellationToken); + return await Worker.IoScheduleFunction(() => Cleanup(repo), cts.Token); } public List ListBackup(string path, CancellationToken cancellationToken) { - IEnumerable ListDate() + async Task> ListDate() { + using var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, + _cancellationTokenSource.Token); var repo = new BackupRepository(BackupUtils.GetRepoFilePath(path)); cancellationToken.ThrowIfCancellationRequested(); - foreach (var entry in BackupUtils.ListBackup(repo.Entries)) - { - yield return entry.DateTime; - } + var entries = + await Worker.IoScheduleFunction(() => BackupUtils.ListBackup(repo.Entries).ToList(), cts.Token); + return entries.Select(entry => entry.DateTime).ToList(); } try { - return ListDate().ToList(); + var task = Task.Run(ListDate, cancellationToken); + return task.Result; } catch (FileNotFoundException) { @@ -84,8 +102,58 @@ IEnumerable ListDate() } } - public bool Restore(string path, DateTime date, CancellationToken cancellationToken) + { + var res = Task.Run( + async () => await Worker.IoScheduleFunction(() => DoRestore(path, date, cancellationToken), + cancellationToken), cancellationToken); + return res.Result; + } + + public void Dispose() + { + Log.LogDebug($"{nameof(BackupService)} Dispose"); + Worker.Dispose(); + _saveDiffSemaphore.Dispose(); + Strategy.Dispose(); + _cancellationTokenSource.Cancel(false); + } + + private async Task CleanupScheduleLoop() + { + var cancellationToken = _cancellationTokenSource.Token; + if (Config.Strategy.Cleanup.ScheduleTimeSpan <= TimeSpan.Zero) + { + Log.LogDebug("CleanupScheduleLoop no need to run"); + return; + } + + var random = new Random(); + await Task.Delay(TimeSpan.FromSeconds(random.NextDouble() * 60), cancellationToken); + + try + { + while (!cancellationToken.IsCancellationRequested) + { + if (_scheduledCleanupStopWatch.Elapsed >= Config.Strategy.Cleanup.ScheduleTimeSpan) + { + await StartCleanup(Main.WorldPath, cancellationToken); + _scheduledCleanupStopWatch.Restart(); + } + + await Task.Delay( + Config.Strategy.Cleanup.ScheduleTimeSpan + TimeSpan.FromSeconds(random.NextDouble() * 60), + cancellationToken); + } + } + catch (OperationCanceledException e) + { + Log.LogDebug("Loop cancelled"); + Log.LogDebug(e.BuildExceptionString(), TraceLevel.Error); + } + } + + private bool DoRestore(string path, DateTime date, CancellationToken cancellationToken) { var repo = new BackupRepository(BackupUtils.GetRepoFilePath(path)); cancellationToken.ThrowIfCancellationRequested(); @@ -128,14 +196,6 @@ public bool Restore(string path, DateTime date, CancellationToken cancellationTo } } - public void Dispose() - { - Log.LogDebug($"{nameof(BackupService)} Dispose"); - Worker.Dispose(); - _saveDiffSemaphore.Dispose(); - Strategy.Dispose(); - } - private IList Cleanup(BackupRepository repo) { var res = new List(); @@ -189,14 +249,20 @@ public async Task BackupSaveAsync(string path, DateTime? dateTime = null, CancellationToken cancellationToken = default) { using var timed = new CancellationTokenSource(); - timed.CancelAfter(TimeSpan.FromMinutes(3)); + if (Config.Internal.BackupTaskTimeoutTimeSpan > TimeSpan.Zero) + { + timed.CancelAfter(Config.Internal.BackupTaskTimeoutTimeSpan); + } + using var tokenSource = - CancellationTokenSource.CreateLinkedTokenSource(Worker.Token, cancellationToken, timed.Token); + CancellationTokenSource.CreateLinkedTokenSource(Worker.Token, cancellationToken, timed.Token, + _cancellationTokenSource.Token); tokenSource.Token.ThrowIfCancellationRequested(); await _saveDiffSemaphore.WaitAsync(tokenSource.Token); async Task IoSchedule(Func func) { + // ReSharper disable once AccessToDisposedClosure return await IoScheduleFunction(func, tokenSource!.Token); } diff --git a/DiffBackup/Backup/BackupStrategy.cs b/DiffBackup/Backup/BackupStrategy.cs index 5d56f91..32a38d5 100644 --- a/DiffBackup/Backup/BackupStrategy.cs +++ b/DiffBackup/Backup/BackupStrategy.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; +using DiffBackup.Backup.Config; using DiffBackup.Logger; #nullable enable @@ -9,11 +10,13 @@ namespace DiffBackup.Backup { public class DefaultBackupStrategy : IBackupStrategy { + public readonly StrategyConfig Config; public readonly ITlog Log; - public DefaultBackupStrategy(ITlog log) + public DefaultBackupStrategy(ITlog log, StrategyConfig config) { Log = log; + Config = config.Clone(); var env = Environment.GetEnvironmentVariables(); // ReSharper disable once InconsistentNaming @@ -29,12 +32,21 @@ public DefaultBackupStrategy(ITlog log) const string TdiffOverwriteFillFactor = nameof(TdiffOverwriteFillFactor); if (env.Contains(TdiffOverwriteFillFactor)) { - FillFactor = float.Parse(Environment.GetEnvironmentVariable(TdiffOverwriteFillFactor)); + FillFactor = double.Parse(Environment.GetEnvironmentVariable(TdiffOverwriteFillFactor)); } } - public TimeSpan ForceFullBackupTimeSpan { get; set; } = TimeSpan.FromDays(7); - public float FillFactor { get; set; } = 0.5f; + public TimeSpan ForceFullBackupTimeSpan + { + get => Config.EveryXTimeSpanDoFullBackup; + set => Config.EveryXTimeSpanDoFullBackup = value; + } + + public double FillFactor + { + get => Config.DiffFillFactorToStartFullBackup; + set => Config.DiffFillFactorToStartFullBackup = value; + } public BackupRepositoryEntry? GetReference(BackupRepository repo, DateTime date) { @@ -97,34 +109,30 @@ public IList ListExpiredEntries(BackupRepository repo, Da } } - CleanupTimeConfig[] worldCleanupTimeConfigs = - { - new CleanupTimeConfig("yearly", TimeSpan.FromDays(365.25), TimeSpan.FromDays(3)), - new CleanupTimeConfig("monthly", TimeSpan.FromDays(30.44), TimeSpan.FromHours(12)), - new CleanupTimeConfig("weekly", TimeSpan.FromDays(7), TimeSpan.FromHours(1)) - }; - CleanupTimeConfig[] diffCleanupTimeConfigs = + foreach (var config in Config.Cleanup.WldFiles) { - new CleanupTimeConfig("yearly", TimeSpan.FromDays(365.25), TimeSpan.MaxValue), - new CleanupTimeConfig("monthly", TimeSpan.FromDays(30.44), TimeSpan.FromMinutes(30)), - new CleanupTimeConfig("weekly", TimeSpan.FromDays(7), TimeSpan.FromMinutes(1)) - }; - + if (config.AgeFilter <= TimeSpan.Zero) + { + continue; + } - foreach (var config in worldCleanupTimeConfigs) - { var marked = ConsumeIEnumerable(MarkOldEntries(backups.Where(backup => !backup.IsDiff), config.AgeFilter, config.KeepBackupEveryTimeSpan)); - Log.LogDebug($"Marked {marked} entries for {config.Text.ToLower()} wld cleanup"); + Log.LogDebug($"Marked {marked} entries for {config.Name} wld cleanup"); } - foreach (var config in diffCleanupTimeConfigs) + foreach (var config in Config.Cleanup.DiffFiles) { + if (config.AgeFilter <= TimeSpan.Zero) + { + continue; + } + var marked = ConsumeIEnumerable(MarkOldEntries(backups.Where(backup => backup.IsDiff), config.AgeFilter, config.KeepBackupEveryTimeSpan)); - Log.LogDebug($"Marked {marked} entries for {config.Text.ToLower()} diff cleanup"); + Log.LogDebug($"Marked {marked} entries for {config.Name} diff cleanup"); } foreach (var entry in backups.Where(backup => !backup.Valid)) @@ -143,19 +151,5 @@ internal static int ConsumeIEnumerable(IEnumerable enumerable) { return enumerable.Count(); } - - public readonly struct CleanupTimeConfig - { - public readonly string Text; - public readonly TimeSpan AgeFilter; - public readonly TimeSpan KeepBackupEveryTimeSpan; - - public CleanupTimeConfig(string text, TimeSpan ageFilter, TimeSpan keepBackupEveryTimeSpan) - { - Text = text; - AgeFilter = ageFilter; - KeepBackupEveryTimeSpan = keepBackupEveryTimeSpan; - } - } } } \ No newline at end of file diff --git a/DiffBackup/Backup/Config/AllConfig.cs b/DiffBackup/Backup/Config/AllConfig.cs new file mode 100644 index 0000000..4ca934e --- /dev/null +++ b/DiffBackup/Backup/Config/AllConfig.cs @@ -0,0 +1,9 @@ +namespace DiffBackup.Backup.Config +{ + public class AllConfig : BaseConfig + { + public int Version { get; set; } = 1; + public StrategyConfig Strategy { get; set; } = new StrategyConfig(); + public InternalConfig Internal { get; set; } = new InternalConfig(); + } +} \ No newline at end of file diff --git a/DiffBackup/Backup/Config/BaseConfig.cs b/DiffBackup/Backup/Config/BaseConfig.cs new file mode 100644 index 0000000..90f6457 --- /dev/null +++ b/DiffBackup/Backup/Config/BaseConfig.cs @@ -0,0 +1,42 @@ +using System; +using Topten.JsonKit_tie_tdiff; + +namespace DiffBackup.Backup.Config +{ + public abstract class BaseConfig : IConfig + { + public BaseConfig() + { + } + + public BaseConfig(BaseConfig copy) + { + Topten.JsonKit_tie_tdiff.Json.CloneInto(this, copy); + } + + public IConfig Clone() + { + return Clone(); + } + + public T Clone() where T : IConfig + { + if (this is T t) + { + return Topten.JsonKit_tie_tdiff.Json.Clone(t); + } + + throw new TypeAccessException(); + } + + public string ToJson(JsonOptions options = JsonOptions.None) + { + return Topten.JsonKit_tie_tdiff.Json.Format(this, options); + } + + public void SetFromJson(string data, JsonOptions options = JsonOptions.None) + { + Topten.JsonKit_tie_tdiff.Json.ParseInto(data, this, options); + } + } +} \ No newline at end of file diff --git a/DiffBackup/Backup/Config/CleanupConfig.cs b/DiffBackup/Backup/Config/CleanupConfig.cs new file mode 100644 index 0000000..433fd5c --- /dev/null +++ b/DiffBackup/Backup/Config/CleanupConfig.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; + +namespace DiffBackup.Backup.Config +{ + public class CleanupConfig : BaseConfig + { + public TimeSpanConfig ScheduleTimeSpan { get; set; } = TimeSpan.FromHours(12); + + public List WldFiles { get; set; } = new List + { + new CleanupTimeConfig("yearly", TimeSpan.FromDays(365), TimeSpan.FromDays(3)), + new CleanupTimeConfig("monthly", TimeSpan.FromDays(30), TimeSpan.FromHours(12)), + new CleanupTimeConfig("weekly", TimeSpan.FromDays(7), TimeSpan.FromHours(1)) + }; + + public List DiffFiles { get; set; } = new List + { + new CleanupTimeConfig("yearly", TimeSpan.FromDays(365), TimeSpan.FromDays(365)), + new CleanupTimeConfig("monthly", TimeSpan.FromDays(30), TimeSpan.FromMinutes(30)), + new CleanupTimeConfig("weekly", TimeSpan.FromDays(7), TimeSpan.FromMinutes(1)) + }; + } +} \ No newline at end of file diff --git a/DiffBackup/Backup/Config/CleanupTimeConfig.cs b/DiffBackup/Backup/Config/CleanupTimeConfig.cs new file mode 100644 index 0000000..04a6d0e --- /dev/null +++ b/DiffBackup/Backup/Config/CleanupTimeConfig.cs @@ -0,0 +1,28 @@ +using System; + +namespace DiffBackup.Backup.Config +{ + public class CleanupTimeConfig : BaseConfig + { + public CleanupTimeConfig() + { + } + + public CleanupTimeConfig(string name, TimeSpan ageFilter, TimeSpan keepBackupEveryTimeSpan) + { + Name = name; + AgeFilter = ageFilter; + KeepBackupEveryTimeSpan = keepBackupEveryTimeSpan; + } + + public CleanupTimeConfig(CleanupTimeConfig copy) : base(copy) + { + } + + public string Name { get; set; } = ""; + + public TimeSpanConfig AgeFilter { get; set; } = TimeSpan.Zero; + + public TimeSpanConfig KeepBackupEveryTimeSpan { get; set; } = TimeSpan.MaxValue; + } +} \ No newline at end of file diff --git a/DiffBackup/Backup/Config/Config.cs b/DiffBackup/Backup/Config/Config.cs deleted file mode 100644 index f7d9d89..0000000 --- a/DiffBackup/Backup/Config/Config.cs +++ /dev/null @@ -1,20 +0,0 @@ -namespace DiffBackup.Backup.Config -{ - public class InternalConfig - { - public double ThrottleSecond { get; set; } = 60; - } - - - public class StrategyConfig - { - public double EveryXDayDoFullBackup { get; set; } = 7; - public double FillFactorToStartFullBackup { get; set; } = 0.5; - } - - public class Config - { - public StrategyConfig Strategy { get; set; } = new StrategyConfig(); - public InternalConfig Internal { get; set; } = new InternalConfig(); - } -} \ No newline at end of file diff --git a/DiffBackup/Backup/Config/IConfig.cs b/DiffBackup/Backup/Config/IConfig.cs new file mode 100644 index 0000000..71e11b7 --- /dev/null +++ b/DiffBackup/Backup/Config/IConfig.cs @@ -0,0 +1,13 @@ +using Topten.JsonKit_tie_tdiff; + +namespace DiffBackup.Backup.Config +{ + public interface IConfig + { + IConfig Clone(); + T Clone() where T : IConfig; + string ToJson(JsonOptions options = JsonOptions.None); + + void SetFromJson(string payload, JsonOptions options = JsonOptions.None); + } +} \ No newline at end of file diff --git a/DiffBackup/Backup/Config/InternalConfig.cs b/DiffBackup/Backup/Config/InternalConfig.cs new file mode 100644 index 0000000..4f7482e --- /dev/null +++ b/DiffBackup/Backup/Config/InternalConfig.cs @@ -0,0 +1,14 @@ +using System; + +namespace DiffBackup.Backup.Config +{ + public class InternalConfig : BaseConfig + { + public TimeSpanConfig ThrottleTimeSpan { get; set; } = TimeSpan.FromSeconds(60); + + public TimeSpanConfig BackupTaskTimeoutTimeSpan { get; set; } = TimeSpan.FromMinutes(3); + + public WorldSaveTrackingStrategy WorldSaveTrackingStrategy { get; set; } = + WorldSaveTrackingStrategy.Default; + } +} \ No newline at end of file diff --git a/DiffBackup/Backup/Config/Json/JsonConverters.cs b/DiffBackup/Backup/Config/Json/JsonConverters.cs new file mode 100644 index 0000000..80137eb --- /dev/null +++ b/DiffBackup/Backup/Config/Json/JsonConverters.cs @@ -0,0 +1,69 @@ +using System; +using Topten.JsonKit_tie_tdiff; + +#nullable enable +namespace DiffBackup.Backup.Config.Json +{ + public static class JsonConverters + { + /// + /// Format: Days.Hours:Minutes:Seconds:Milliseconds + /// + public const string TimeSpanFormatString = @"d\.hh\:mm\:ss\:FFF"; + + private static readonly object Lock = new object(); + + static JsonConverters() + { + Install(); + } + + public static bool Installed { get; private set; } + + public static bool Install() + { + if (!Installed) + { + lock (Lock) + { + if (!Installed) + { + Topten.JsonKit_tie_tdiff.Json.RegisterFormatter(WriteJson); + Topten.JsonKit_tie_tdiff.Json.RegisterParser(TimeSpanParseJson); + Topten.JsonKit_tie_tdiff.Json.RegisterFormatter(WriteJson); + Topten.JsonKit_tie_tdiff.Json.RegisterParser(WorldSaveTrackingStrategyParseJson); + Installed = true; + return true; + } + } + } + + return false; + } + + private static TimeSpanConfig TimeSpanParseJson(object literal) + { + return TimeSpanConfig.ParseJson(literal.ToString()); + } + + private static void WriteJson(IJsonWriter writer, TimeSpanConfig value) + { + writer.WriteStringLiteral(value.FormatJson()); + } + + private static void WriteJson(IJsonWriter writer, WorldSaveTrackingStrategy value) + { + writer.WriteStringLiteral(value.ToString()); + } + + private static WorldSaveTrackingStrategy WorldSaveTrackingStrategyParseJson(object literal) + { + if (Enum.TryParse((string) literal, out WorldSaveTrackingStrategy res)) + { + return res; + } + + return WorldSaveTrackingStrategy.SaveEventListener; + } + } +} \ No newline at end of file diff --git a/DiffBackup/Backup/Config/Json/TShockConfigHandler.cs b/DiffBackup/Backup/Config/Json/TShockConfigHandler.cs new file mode 100644 index 0000000..ce08fc3 --- /dev/null +++ b/DiffBackup/Backup/Config/Json/TShockConfigHandler.cs @@ -0,0 +1,65 @@ +using System.IO; +using System.Text; +using Topten.JsonKit_tie_tdiff; +using TShockAPI; + +#nullable enable +namespace DiffBackup.Backup.Config.Json +{ + public class TShockConfigHandler + { + public readonly string BasePath; + + public TShockConfigHandler(string basePath) + { + JsonConverters.Install(); + BasePath = Path.GetFullPath(basePath); + } + + public TShockConfigHandler() : this(TShock.SavePath) + { + } + + + public T LoadJson(string fileName) where T : class?, new() + { + var path = Path.Combine(BasePath, fileName); + var content = File.ReadAllText(path, Encoding.UTF8); + return Topten.JsonKit_tie_tdiff.Json.Parse(content, JsonOptions.NonStrictParser); + } + + public bool TryLoadJson(string fileName, out T result) + where T : new() + { + var path = GetFullPath(fileName); + + if (!File.Exists(path)) + { + result = new T(); + return false; + } + + var content = File.ReadAllText(path, Encoding.UTF8); + if (string.IsNullOrWhiteSpace(content)) + { + result = new T(); + return false; + } + + result = Topten.JsonKit_tie_tdiff.Json.Parse(content, JsonOptions.NonStrictParser); + return true; + } + + public void SaveConfig(T config, string fileName) + { + var path = GetFullPath(fileName); + var payload = Topten.JsonKit_tie_tdiff.Json.Format(config); + File.WriteAllText(path, payload, Encoding.UTF8); + } + + public string GetFullPath(string fileName) + { + return Path.Combine(BasePath, fileName); + } + } +} \ No newline at end of file diff --git a/DiffBackup/Backup/Config/StrategyConfig.cs b/DiffBackup/Backup/Config/StrategyConfig.cs new file mode 100644 index 0000000..aa64db5 --- /dev/null +++ b/DiffBackup/Backup/Config/StrategyConfig.cs @@ -0,0 +1,12 @@ +using System; + +namespace DiffBackup.Backup.Config +{ + public class StrategyConfig : BaseConfig + { + public TimeSpanConfig EveryXTimeSpanDoFullBackup { get; set; } = TimeSpan.FromDays(7); + + public double DiffFillFactorToStartFullBackup { get; set; } = 0.5; + public CleanupConfig Cleanup { get; set; } = new CleanupConfig(); + } +} \ No newline at end of file diff --git a/DiffBackup/Backup/Config/TimeSpanConfig.cs b/DiffBackup/Backup/Config/TimeSpanConfig.cs new file mode 100644 index 0000000..e42898d --- /dev/null +++ b/DiffBackup/Backup/Config/TimeSpanConfig.cs @@ -0,0 +1,35 @@ +using System; +using DiffBackup.Backup.Config.Json; + +namespace DiffBackup.Backup.Config +{ + public readonly struct TimeSpanConfig + { + public readonly TimeSpan Value; + + public TimeSpanConfig(TimeSpan value) + { + Value = value; + } + + public static implicit operator TimeSpan(TimeSpanConfig input) + { + return input.Value; + } + + public static implicit operator TimeSpanConfig(TimeSpan input) + { + return new TimeSpanConfig(input); + } + + public string FormatJson() + { + return Value.ToString(JsonConverters.TimeSpanFormatString); + } + + public static TimeSpanConfig ParseJson(string literal) + { + return TimeSpan.ParseExact(literal, JsonConverters.TimeSpanFormatString, null); + } + } +} \ No newline at end of file diff --git a/DiffBackup/Backup/Config/WorldSaveTrackingStrategy.cs b/DiffBackup/Backup/Config/WorldSaveTrackingStrategy.cs new file mode 100644 index 0000000..68dc03b --- /dev/null +++ b/DiffBackup/Backup/Config/WorldSaveTrackingStrategy.cs @@ -0,0 +1,14 @@ +using System; + +namespace DiffBackup +{ + [Flags] + public enum WorldSaveTrackingStrategy : byte + { + None = 0, + FileSystemWatcher = 1, + SaveEventListener = 1 << 1, + All = FileSystemWatcher | SaveEventListener, + Default = SaveEventListener + } +} \ No newline at end of file diff --git a/DiffBackup/DiffBackup.csproj b/DiffBackup/DiffBackup.csproj index b9043fd..e45070b 100644 --- a/DiffBackup/DiffBackup.csproj +++ b/DiffBackup/DiffBackup.csproj @@ -41,6 +41,7 @@ + ..\TShock\TerrariaServer.exe @@ -116,22 +117,112 @@ HumanDateParser\TokenKind.cs + + JsonKit\CancelReaderException.cs + + + JsonKit\DecoratingActivator.cs + + + JsonKit\Emit.cs + + + JsonKit\IDictionaryExtensions.cs + + + JsonKit\IJsonLoaded.cs + + + JsonKit\IJsonLoadField.cs + + + JsonKit\IJsonLoading.cs + + + JsonKit\IJsonReader.cs + + + JsonKit\IJsonWriter.cs + + + JsonKit\IJsonWriting.cs + + + JsonKit\IJsonWritten.cs + + + JsonKit\JsonAttribute.cs + + + JsonKit\JsonExcludeAttribute.cs + + + JsonKit\JsonKit.cs + + + JsonKit\JsonMemberInfo.cs + + + JsonKit\JsonOptions.cs + + + JsonKit\JsonParseException.cs + + + JsonKit\JsonReader.cs + + + JsonKit\JsonUnknownAttribute.cs + + + JsonKit\JsonWriter.cs + + + JsonKit\LineOffset.cs + + + JsonKit\LiteralKind.cs + + + JsonKit\ReflectionInfo.cs + + + JsonKit\ThreadSafeCache.cs + + + JsonKit\Token.cs + + + JsonKit\Tokenizer.cs + + + JsonKit\Utils.cs + - + + + + + + + + + + + - + -