Add support for creating new files

Now it's complete, bug-hunting begins
This commit is contained in:
JustArchi
2016-10-07 01:36:02 +02:00
parent 562cf24491
commit fc9ae8dd7b
4 changed files with 178 additions and 112 deletions

View File

@@ -23,6 +23,7 @@
*/
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Linq;
@@ -33,7 +34,18 @@ using ArchiSteamFarm.JSON;
namespace ArchiSteamFarm {
internal static class ASF {
internal sealed class BotConfigEventArgs : EventArgs {
internal readonly BotConfig BotConfig;
internal BotConfigEventArgs(BotConfig botConfig = null) {
BotConfig = botConfig;
}
}
private static readonly ConcurrentDictionary<Bot, DateTime> LastWriteTimes = new ConcurrentDictionary<Bot, DateTime>();
private static Timer AutoUpdatesTimer;
private static FileSystemWatcher FileSystemWatcher;
internal static async Task CheckForUpdate(bool updateOverride = false) {
string exeFile = Assembly.GetEntryAssembly().Location;
@@ -200,5 +212,154 @@ namespace ArchiSteamFarm {
Program.Exit();
}
}
internal static void InitFileWatcher() {
if (FileSystemWatcher != null) {
return;
}
FileSystemWatcher = new FileSystemWatcher(SharedInfo.ConfigDirectory, "*json") {
NotifyFilter = NotifyFilters.FileName | NotifyFilters.LastWrite
};
FileSystemWatcher.Changed += OnChanged;
FileSystemWatcher.Created += OnCreated;
FileSystemWatcher.Deleted += OnDeleted;
FileSystemWatcher.Renamed += OnRenamed;
FileSystemWatcher.EnableRaisingEvents = true;
}
private static string GetBotNameFromConfigFileName(string fileName) {
if (string.IsNullOrEmpty(fileName)) {
Logging.LogNullError(nameof(fileName));
return null;
}
string botName = Path.GetFileNameWithoutExtension(fileName);
if (!string.IsNullOrEmpty(botName)) {
return !botName.Equals(SharedInfo.ASF) ? botName : null;
}
Logging.LogNullError(nameof(botName));
return null;
}
private static Bot GetBotFromConfigFileName(string fileName) {
if (string.IsNullOrEmpty(fileName)) {
Logging.LogNullError(nameof(fileName));
return null;
}
string botName = GetBotNameFromConfigFileName(fileName);
if (string.IsNullOrEmpty(botName)) {
Logging.LogNullError(nameof(botName));
return null;
}
Bot bot;
return Bot.Bots.TryGetValue(botName, out bot) ? bot : null;
}
private static async Task CreateBot(string botName) {
if (string.IsNullOrEmpty(botName)) {
Logging.LogNullError(nameof(botName));
return;
}
if (Bot.Bots.ContainsKey(botName)) {
return;
}
// It's entirely possible that some process is still accessing our file, allow at least a second before trying to read it
await Task.Delay(1000).ConfigureAwait(false);
if (Bot.Bots.ContainsKey(botName)) {
return;
}
new Bot(botName).Forget();
}
private static async void OnChanged(object sender, FileSystemEventArgs e) {
if ((sender == null) || (e == null)) {
Logging.LogNullError(nameof(sender) + " || " + nameof(e));
return;
}
string botName = GetBotNameFromConfigFileName(e.Name);
if (string.IsNullOrEmpty(botName)) {
return;
}
Bot bot;
if (!Bot.Bots.TryGetValue(botName, out bot)) {
CreateBot(botName).Forget();
return;
}
DateTime lastWriteTime = File.GetLastWriteTime(e.FullPath);
DateTime savedLastWriteTime;
if (LastWriteTimes.TryGetValue(bot, out savedLastWriteTime)) {
if (savedLastWriteTime >= lastWriteTime) {
return;
}
}
LastWriteTimes[bot] = lastWriteTime;
// It's entirely possible that some process is still accessing our file, allow at least a second before trying to read it
await Task.Delay(1000).ConfigureAwait(false);
// It's also possible that we got some other event in the meantime
if (LastWriteTimes.TryGetValue(bot, out savedLastWriteTime)) {
if (lastWriteTime != savedLastWriteTime) {
return;
}
if (LastWriteTimes.TryRemove(bot, out savedLastWriteTime)) {
if (lastWriteTime != savedLastWriteTime) {
return;
}
}
}
bot.OnNewConfigLoaded(new BotConfigEventArgs(BotConfig.Load(e.FullPath))).Forget();
}
private static void OnCreated(object sender, FileSystemEventArgs e) {
if ((sender == null) || (e == null)) {
Logging.LogNullError(nameof(sender) + " || " + nameof(e));
return;
}
string botName = GetBotNameFromConfigFileName(e.Name);
if (string.IsNullOrEmpty(botName)) {
return;
}
CreateBot(botName).Forget();
}
private static void OnDeleted(object sender, FileSystemEventArgs e) {
if ((sender == null) || (e == null)) {
Logging.LogNullError(nameof(sender) + " || " + nameof(e));
return;
}
Bot bot = GetBotFromConfigFileName(e.Name);
bot?.OnNewConfigLoaded(new BotConfigEventArgs()).Forget();
}
private static void OnRenamed(object sender, RenamedEventArgs e) {
if ((sender == null) || (e == null)) {
Logging.LogNullError(nameof(sender) + " || " + nameof(e));
return;
}
Bot bot = GetBotFromConfigFileName(e.OldName);
bot?.OnNewConfigLoaded(new BotConfigEventArgs()).Forget();
}
}
}

View File

@@ -256,22 +256,9 @@ namespace ArchiSteamFarm {
Initialize().Forget();
}
private async Task Initialize() {
BotConfig.NewConfigLoaded += OnNewConfigLoaded;
BotConfig.InitializeWatcher();
if (!BotConfig.Enabled) {
Logging.LogGenericInfo("Not starting this instance because it's disabled in config file", BotName);
return;
}
// Start
await Start().ConfigureAwait(false);
}
private async void OnNewConfigLoaded(object sender, BotConfig.BotConfigEventArgs args) {
if ((sender == null) || (args == null)) {
Logging.LogNullError(nameof(sender) + " || " + nameof(args), BotName);
internal async Task OnNewConfigLoaded(ASF.BotConfigEventArgs args) {
if (args == null) {
Logging.LogNullError(nameof(args), BotName);
return;
}
@@ -284,15 +271,12 @@ namespace ArchiSteamFarm {
return;
}
await InitializationSemaphore.WaitAsync().ConfigureAwait(false);
try {
if (args.BotConfig == BotConfig) {
return;
}
Stop();
BotConfig.NewConfigLoaded -= OnNewConfigLoaded;
BotConfig = args.BotConfig;
CardsFarmer.Paused = BotConfig.Paused;
@@ -341,11 +325,19 @@ namespace ArchiSteamFarm {
}
}
private async Task Initialize() {
if (!BotConfig.Enabled) {
Logging.LogGenericInfo("Not starting this instance because it's disabled in config file", BotName);
return;
}
// Start
await Start().ConfigureAwait(false);
}
public void Dispose() {
// Those are objects that are always being created if constructor doesn't throw exception
ArchiWebHandler.Dispose();
BotConfig.NewConfigLoaded -= OnNewConfigLoaded;
BotConfig.Dispose();
CardsFarmer.Dispose();
HeartBeatTimer.Dispose();
HandledGifts.Dispose();
@@ -2178,6 +2170,7 @@ namespace ArchiSteamFarm {
case EResult.ServiceUnavailable:
case EResult.Timeout:
case EResult.TryAnotherCM:
case EResult.TwoFactorCodeMismatch:
Logging.LogGenericWarning("Unable to login to Steam: " + callback.Result + " / " + callback.ExtendedResult, BotName);
break;
default: // Unexpected result, shutdown immediately

View File

@@ -28,22 +28,13 @@ using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
namespace ArchiSteamFarm {
[SuppressMessage("ReSharper", "ClassCannotBeInstantiated")]
[SuppressMessage("ReSharper", "ClassNeverInstantiated.Global")]
[SuppressMessage("ReSharper", "ConvertToConstant.Local")]
[SuppressMessage("ReSharper", "ConvertToConstant.Global")]
internal sealed class BotConfig : IDisposable {
internal sealed class BotConfigEventArgs : EventArgs {
internal readonly BotConfig BotConfig;
internal BotConfigEventArgs(BotConfig botConfig = null) {
BotConfig = botConfig;
}
}
internal sealed class BotConfig {
internal enum EFarmingOrder : byte {
Unordered,
AppIDsAscending,
@@ -137,12 +128,6 @@ namespace ArchiSteamFarm {
[JsonProperty(Required = Required.DisallowNull)]
private readonly CryptoHelper.ECryptoMethod PasswordFormat = CryptoHelper.ECryptoMethod.PlainText;
internal event EventHandler<BotConfigEventArgs> NewConfigLoaded;
private string FilePath;
private FileSystemWatcher FileSystemWatcher;
private DateTime LastWriteTime = DateTime.MinValue;
internal static BotConfig Load(string filePath) {
if (string.IsNullOrEmpty(filePath)) {
Logging.LogNullError(nameof(filePath));
@@ -167,8 +152,6 @@ namespace ArchiSteamFarm {
return null;
}
botConfig.FilePath = filePath;
// Support encrypted passwords
if ((botConfig.PasswordFormat != CryptoHelper.ECryptoMethod.PlainText) && !string.IsNullOrEmpty(botConfig.SteamPassword)) {
// In worst case password will result in null, which will have to be corrected by user during runtime
@@ -192,78 +175,5 @@ namespace ArchiSteamFarm {
// This constructor is used only by deserializer
private BotConfig() { }
internal void InitializeWatcher() {
if (FileSystemWatcher != null) {
return;
}
if (string.IsNullOrEmpty(FilePath)) {
Logging.LogNullError(nameof(FilePath));
return;
}
string fileDirectory = Path.GetDirectoryName(FilePath);
if (string.IsNullOrEmpty(fileDirectory)) {
Logging.LogNullError(nameof(fileDirectory));
return;
}
string fileName = Path.GetFileName(FilePath);
if (string.IsNullOrEmpty(fileName)) {
Logging.LogNullError(nameof(fileName));
return;
}
FileSystemWatcher = new FileSystemWatcher(fileDirectory, fileName) {
NotifyFilter = NotifyFilters.FileName | NotifyFilters.LastWrite
};
FileSystemWatcher.Changed += OnChanged;
FileSystemWatcher.Deleted += OnDeleted;
FileSystemWatcher.Renamed += OnRenamed;
FileSystemWatcher.EnableRaisingEvents = true;
}
private async void OnChanged(object sender, FileSystemEventArgs e) {
if (NewConfigLoaded == null) {
return;
}
DateTime lastWriteTime;
lock (FileSystemWatcher) {
lastWriteTime = File.GetLastWriteTime(FilePath);
if (LastWriteTime == lastWriteTime) {
return;
}
LastWriteTime = lastWriteTime;
}
// It's entirely possible that some process is still accessing our file, allow at least a second before trying to read it
await Task.Delay(1000).ConfigureAwait(false);
// It's also possible that we got some other event in the meantime
if (lastWriteTime != LastWriteTime) {
return;
}
NewConfigLoaded?.Invoke(this, new BotConfigEventArgs(Load(FilePath)));
}
private void OnDeleted(object sender, FileSystemEventArgs e) => NewConfigLoaded?.Invoke(this, new BotConfigEventArgs());
private void OnRenamed(object sender, RenamedEventArgs e) => NewConfigLoaded?.Invoke(this, new BotConfigEventArgs());
public void Dispose() {
if (FileSystemWatcher == null) {
return;
}
FileSystemWatcher.Changed -= OnChanged;
FileSystemWatcher.Deleted -= OnDeleted;
FileSystemWatcher.Renamed -= OnRenamed;
FileSystemWatcher.Dispose();
}
}
}

View File

@@ -350,6 +350,8 @@ namespace ArchiSteamFarm {
new Bot(botName).Forget();
}
ASF.InitFileWatcher();
}
private static void Main(string[] args) {