diff --git a/ArchiSteamFarm/Bot.cs b/ArchiSteamFarm/Bot.cs index dc01fe4c0..288dc0905 100644 --- a/ArchiSteamFarm/Bot.cs +++ b/ArchiSteamFarm/Bot.cs @@ -75,12 +75,14 @@ namespace ArchiSteamFarm { return result; } - internal static void ShutdownAllBots() { + internal static async Task ShutdownAllBots() { + List tasks = new List(); lock (Bots) { foreach (Bot bot in Bots) { - bot.Shutdown(); + tasks.Add(Task.Run(async () => await bot.Shutdown().ConfigureAwait(false))); } } + await Task.WhenAll(tasks).ConfigureAwait(false); } internal Bot(string botName) { @@ -203,26 +205,27 @@ namespace ArchiSteamFarm { Task.Run(() => HandleCallbacks()); } - internal void Stop() { + internal async Task Stop() { if (!IsRunning) { return; } + await CardsFarmer.StopFarming().ConfigureAwait(false); SteamClient.Disconnect(); IsRunning = false; } - internal void Shutdown() { - Stop(); + internal async Task Shutdown() { + await Stop().ConfigureAwait(false); lock (Bots) { Bots.Remove(this); } Program.OnBotShutdown(this); } - internal void OnFarmingFinished() { + internal async Task OnFarmingFinished() { if (ShutdownOnFarmingFinished) { - Shutdown(); + await Shutdown().ConfigureAwait(false); } } @@ -312,7 +315,7 @@ namespace ArchiSteamFarm { } } - private void OnFriendMsg(SteamFriends.FriendMsgCallback callback) { + private async void OnFriendMsg(SteamFriends.FriendMsgCallback callback) { if (callback == null) { return; } @@ -334,6 +337,15 @@ namespace ArchiSteamFarm { if (message.Length == 17 && message[5] == '-' && message[11] == '-') { ArchiHandler.RedeemKey(message); } + + switch (message) { + case "!farm": + await CardsFarmer.StartFarming().ConfigureAwait(false); + break; + case "!exit": + await Shutdown().ConfigureAwait(false); + break; + } } private void OnPersonaState(SteamFriends.PersonaStateCallback callback) { @@ -405,13 +417,13 @@ namespace ArchiSteamFarm { case EResult.Timeout: case EResult.TryAnotherCM: Logging.LogGenericWarning(BotName, "Unable to login to Steam: " + callback.Result + " / " + callback.ExtendedResult + ", retrying..."); - Stop(); + await Stop().ConfigureAwait(false); Thread.Sleep(5000); Start(); break; default: Logging.LogGenericWarning(BotName, "Unable to login to Steam: " + callback.Result + " / " + callback.ExtendedResult); - Shutdown(); + await Shutdown().ConfigureAwait(false); break; } } diff --git a/ArchiSteamFarm/CardsFarmer.cs b/ArchiSteamFarm/CardsFarmer.cs index 6c8a0f92c..a3e2f4487 100644 --- a/ArchiSteamFarm/CardsFarmer.cs +++ b/ArchiSteamFarm/CardsFarmer.cs @@ -31,20 +31,32 @@ namespace ArchiSteamFarm { internal class CardsFarmer { private const byte StatusCheckSleep = 5; // In minutes, how long to wait before checking the appID again + private readonly ManualResetEvent FarmResetEvent = new ManualResetEvent(false); + private readonly SemaphoreSlim Semaphore = new SemaphoreSlim(1); private readonly Bot Bot; - private bool NowFarming; - private readonly AutoResetEvent AutoResetEvent = new AutoResetEvent(false); + + private volatile bool NowFarming = false; internal CardsFarmer(Bot bot) { Bot = bot; } internal async Task StartFarming() { + await StopFarming().ConfigureAwait(false); + + await Semaphore.WaitAsync().ConfigureAwait(false); + + if (NowFarming) { + Semaphore.Release(); + return; + } + Logging.LogGenericInfo(Bot.BotName, "Checking badges..."); // Find the number of badge pages HtmlDocument badgesDocument = await Bot.ArchiWebHandler.GetBadgePage(1).ConfigureAwait(false); if (badgesDocument == null) { Logging.LogGenericWarning(Bot.BotName, "Could not get badges information, farming is stopped!"); + Semaphore.Release(); return; } @@ -104,7 +116,26 @@ namespace ArchiSteamFarm { } Logging.LogGenericInfo(Bot.BotName, "Farming finished!"); - Bot.OnFarmingFinished(); + await Bot.OnFarmingFinished().ConfigureAwait(false); + } + + internal async Task StopFarming() { + await Semaphore.WaitAsync().ConfigureAwait(false); + + if (!NowFarming) { + Semaphore.Release(); + return; + } + + Logging.LogGenericInfo(Bot.BotName, "Sending signal to stop farming"); + FarmResetEvent.Set(); + while (NowFarming) { + Logging.LogGenericInfo(Bot.BotName, "Waiting for reaction..."); + Thread.Sleep(1000); + } + FarmResetEvent.Reset(); + Logging.LogGenericInfo(Bot.BotName, "Farming stopped!"); + Semaphore.Release(); } private async Task ShouldFarm(ulong appID) { @@ -120,12 +151,6 @@ namespace ArchiSteamFarm { } private async Task Farm(ulong appID) { - if (NowFarming) { - AutoResetEvent.Set(); - Thread.Sleep(1000); - AutoResetEvent.Reset(); - } - bool success = true; bool? keepFarming = await ShouldFarm(appID).ConfigureAwait(false); while (keepFarming == null || keepFarming.Value) { @@ -133,17 +158,20 @@ namespace ArchiSteamFarm { NowFarming = true; Logging.LogGenericInfo(Bot.BotName, "Now farming: " + appID); Bot.PlayGame(appID); + Semaphore.Release(); // We're farming, allow other tasks to shut us down + } else { + Logging.LogGenericInfo(Bot.BotName, "Still farming: " + appID); } - if (AutoResetEvent.WaitOne(1000 * 60 * StatusCheckSleep)) { + if (FarmResetEvent.WaitOne(1000 * 60 * StatusCheckSleep)) { success = false; break; } keepFarming = await ShouldFarm(appID).ConfigureAwait(false); } - Logging.LogGenericInfo(Bot.BotName, "Stopped farming: " + appID); Bot.PlayGame(0); NowFarming = false; + Logging.LogGenericInfo(Bot.BotName, "Stopped farming: " + appID); return success; } } diff --git a/ArchiSteamFarm/Program.cs b/ArchiSteamFarm/Program.cs index caa045b70..a1a42b7d6 100644 --- a/ArchiSteamFarm/Program.cs +++ b/ArchiSteamFarm/Program.cs @@ -66,11 +66,16 @@ namespace ArchiSteamFarm { Logging.LogGenericNotice("", "Remote version: " + remoteVersion); Logging.LogGenericNotice("", "Consider updating yourself!"); Thread.Sleep(5000); + } else if (localVersion.CompareTo(remoteVersion) > 0) { + Logging.LogGenericNotice("", "You're currently using pre-release version!"); + Logging.LogGenericNotice("", "Local version: " + localVersion); + Logging.LogGenericNotice("", "Remote version: " + remoteVersion); + Logging.LogGenericNotice("", "Be careful!"); } } - internal static void Exit(int exitCode = 0) { - Bot.ShutdownAllBots(); + internal static async Task Exit(int exitCode = 0) { + await Bot.ShutdownAllBots().ConfigureAwait(false); Environment.Exit(exitCode); } @@ -121,8 +126,8 @@ namespace ArchiSteamFarm { if (!Directory.Exists(ConfigDirectoryPath)) { Logging.LogGenericError("Main", "Config directory doesn't exist!"); Console.ReadLine(); - Exit(1); - } + Task.Run(async () => await Exit(1).ConfigureAwait(false)).Wait(); + } foreach (var configFile in Directory.EnumerateFiles(ConfigDirectoryPath, "*.xml")) { string botName = Path.GetFileNameWithoutExtension(configFile); diff --git a/ArchiSteamFarm/Trading.cs b/ArchiSteamFarm/Trading.cs index 7abd263b3..116ddd74c 100644 --- a/ArchiSteamFarm/Trading.cs +++ b/ArchiSteamFarm/Trading.cs @@ -28,9 +28,9 @@ using System.Threading.Tasks; namespace ArchiSteamFarm { internal sealed class Trading { - private Bot Bot; + private readonly Bot Bot; + private readonly SemaphoreSlim Semaphore = new SemaphoreSlim(1); private volatile byte ParsingTasks = 0; - private SemaphoreSlim semaphore = new SemaphoreSlim(1); internal Trading(Bot bot) { Bot = bot; @@ -44,7 +44,7 @@ namespace ArchiSteamFarm { } private async Task ParseActiveTrades() { - await semaphore.WaitAsync().ConfigureAwait(false); + await Semaphore.WaitAsync().ConfigureAwait(false); List tradeOffers = Bot.ArchiWebHandler.GetTradeOffers(); if (tradeOffers != null) { @@ -62,7 +62,7 @@ namespace ArchiSteamFarm { } ParsingTasks--; - semaphore.Release(); + Semaphore.Release(); } private async Task ParseTrade(SteamTradeOffer tradeOffer) { diff --git a/ArchiSteamFarm/config/example.xml b/ArchiSteamFarm/config/example.xml index 4d9268a24..67c11fe69 100644 --- a/ArchiSteamFarm/config/example.xml +++ b/ArchiSteamFarm/config/example.xml @@ -39,7 +39,7 @@ - +