diff --git a/ArchiSteamFarm/ArchiSteamFarm.csproj b/ArchiSteamFarm/ArchiSteamFarm.csproj
index 1d4586cfd..2d8670907 100644
--- a/ArchiSteamFarm/ArchiSteamFarm.csproj
+++ b/ArchiSteamFarm/ArchiSteamFarm.csproj
@@ -39,6 +39,7 @@
DEBUG;TRACE
prompt
3
+ MinimumRecommendedRules.ruleset
AnyCPU
diff --git a/ArchiSteamFarm/ArchiWebHandler.cs b/ArchiSteamFarm/ArchiWebHandler.cs
index 698113724..d5d99478a 100644
--- a/ArchiSteamFarm/ArchiWebHandler.cs
+++ b/ArchiSteamFarm/ArchiWebHandler.cs
@@ -36,7 +36,7 @@ using System.Threading;
using ArchiSteamFarm.JSON;
namespace ArchiSteamFarm {
- internal sealed class ArchiWebHandler {
+ internal sealed class ArchiWebHandler : IDisposable {
private const string SteamCommunityHost = "steamcommunity.com";
private const byte MinSessionTTL = 15; // Assume session is valid for at least that amount of seconds
@@ -161,6 +161,8 @@ namespace ArchiSteamFarm {
WebBrowser = new WebBrowser(bot.BotName);
}
+ public void Dispose() => SessionSemaphore.Dispose();
+
internal void OnDisconnected() => Ready = false;
internal async Task Init(ulong steamID, EUniverse universe, string webAPIUserNonce, string parentalPin) {
diff --git a/ArchiSteamFarm/Bot.cs b/ArchiSteamFarm/Bot.cs
index fcb51a961..7a393d68f 100755
--- a/ArchiSteamFarm/Bot.cs
+++ b/ArchiSteamFarm/Bot.cs
@@ -37,7 +37,7 @@ using System.Text.RegularExpressions;
using ArchiSteamFarm.JSON;
namespace ArchiSteamFarm {
- internal sealed class Bot {
+ internal sealed class Bot : IDisposable {
private const ulong ArchiSCFarmGroup = 103582791440160998;
private const ushort CallbackSleep = 500; // In miliseconds
private const ushort MaxSteamMessageLength = 2048;
@@ -256,6 +256,32 @@ namespace ArchiSteamFarm {
Start().Forget();
}
+ public void Dispose() {
+ GiftsSemaphore.Dispose();
+ LoginSemaphore.Dispose();
+ HandledGifts.Dispose();
+
+ if (AcceptConfirmationsTimer != null) {
+ AcceptConfirmationsTimer.Dispose();
+ }
+
+ if (ArchiWebHandler != null) {
+ ArchiWebHandler.Dispose();
+ }
+
+ if (CardsFarmer != null) {
+ CardsFarmer.Dispose();
+ }
+
+ if (SendItemsTimer != null) {
+ SendItemsTimer.Dispose();
+ }
+
+ if (Trading != null) {
+ Trading.Dispose();
+ }
+ }
+
internal async Task AcceptConfirmations(bool accept, Steam.ConfirmationDetails.EType acceptedType = Steam.ConfirmationDetails.EType.Unknown, ulong acceptedSteamID = 0, HashSet acceptedTradeIDs = null) {
if (BotDatabase.MobileAuthenticator == null) {
return;
diff --git a/ArchiSteamFarm/BotDatabase.cs b/ArchiSteamFarm/BotDatabase.cs
index b4b449a05..a6996e201 100644
--- a/ArchiSteamFarm/BotDatabase.cs
+++ b/ArchiSteamFarm/BotDatabase.cs
@@ -82,6 +82,8 @@ namespace ArchiSteamFarm {
}
}
+ private readonly object FileLock = new object();
+
private string FilePath;
internal static BotDatabase Load(string filePath) {
@@ -133,7 +135,7 @@ namespace ArchiSteamFarm {
return;
}
- lock (FilePath) {
+ lock (FileLock) {
for (byte i = 0; i < 5; i++) {
try {
File.WriteAllText(FilePath, json);
diff --git a/ArchiSteamFarm/CardsFarmer.cs b/ArchiSteamFarm/CardsFarmer.cs
index 815582b31..a94eb6b24 100755
--- a/ArchiSteamFarm/CardsFarmer.cs
+++ b/ArchiSteamFarm/CardsFarmer.cs
@@ -34,7 +34,7 @@ using System.Threading.Tasks;
using Newtonsoft.Json;
namespace ArchiSteamFarm {
- internal sealed class CardsFarmer {
+ internal sealed class CardsFarmer : IDisposable {
internal const byte MaxGamesPlayedConcurrently = 32; // This is limit introduced by Steam Network
[JsonProperty]
@@ -70,6 +70,13 @@ namespace ArchiSteamFarm {
}
}
+ public void Dispose() {
+ CurrentGamesFarming.Dispose();
+ FarmResetEvent.Dispose();
+ FarmingSemaphore.Dispose();
+ Timer.Dispose();
+ }
+
internal async Task SwitchToManualMode(bool manualMode) {
if (ManualMode == manualMode) {
return;
diff --git a/ArchiSteamFarm/Debugging.cs b/ArchiSteamFarm/Debugging.cs
index 0daeecdd7..922262671 100644
--- a/ArchiSteamFarm/Debugging.cs
+++ b/ArchiSteamFarm/Debugging.cs
@@ -40,6 +40,7 @@ namespace ArchiSteamFarm {
internal static bool NetHookAlreadyInitialized { get; set; }
internal sealed class DebugListener : IDebugListener {
+ private readonly object FileLock = new object();
private readonly string FilePath;
internal DebugListener(string filePath) {
@@ -56,7 +57,7 @@ namespace ArchiSteamFarm {
return;
}
- lock (FilePath) {
+ lock (FileLock) {
try {
File.AppendAllText(FilePath, category + " | " + msg + Environment.NewLine);
} catch (Exception e) {
diff --git a/ArchiSteamFarm/GlobalDatabase.cs b/ArchiSteamFarm/GlobalDatabase.cs
index 322bc0f54..b9e1aaa55 100644
--- a/ArchiSteamFarm/GlobalDatabase.cs
+++ b/ArchiSteamFarm/GlobalDatabase.cs
@@ -47,6 +47,8 @@ namespace ArchiSteamFarm {
[JsonProperty(Required = Required.DisallowNull)]
private uint _CellID;
+ private readonly object FileLock = new object();
+
private string FilePath;
internal static GlobalDatabase Load(string filePath) {
@@ -98,7 +100,7 @@ namespace ArchiSteamFarm {
return;
}
- lock (FilePath) {
+ lock (FileLock) {
for (byte i = 0; i < 5; i++) {
try {
File.WriteAllText(FilePath, json);
diff --git a/ArchiSteamFarm/Trading.cs b/ArchiSteamFarm/Trading.cs
index 69e23d353..6da41e033 100644
--- a/ArchiSteamFarm/Trading.cs
+++ b/ArchiSteamFarm/Trading.cs
@@ -31,7 +31,7 @@ using System.Threading.Tasks;
using ArchiSteamFarm.JSON;
namespace ArchiSteamFarm {
- internal sealed class Trading {
+ internal sealed class Trading : IDisposable {
private enum ParseTradeResult : byte {
[SuppressMessage("ReSharper", "UnusedMember.Local")]
Unknown,
@@ -69,6 +69,11 @@ namespace ArchiSteamFarm {
Bot = bot;
}
+ public void Dispose() {
+ IgnoredTrades.Dispose();
+ TradesSemaphore.Dispose();
+ }
+
internal void OnDisconnected() => IgnoredTrades.ClearAndTrim();
internal async Task CheckTrades() {
diff --git a/ArchiSteamFarm/WCF.cs b/ArchiSteamFarm/WCF.cs
index 83b9bd761..b20d2b078 100644
--- a/ArchiSteamFarm/WCF.cs
+++ b/ArchiSteamFarm/WCF.cs
@@ -34,7 +34,7 @@ namespace ArchiSteamFarm {
string HandleCommand(string input);
}
- internal sealed class WCF : IWCF {
+ internal sealed class WCF : IWCF, IDisposable {
private static string URL = "http://localhost:1242/ASF";
@@ -52,6 +52,38 @@ namespace ArchiSteamFarm {
URL = "http://" + Program.GlobalConfig.WCFHostname + ":" + Program.GlobalConfig.WCFPort + "/ASF";
}
+ public string HandleCommand(string input) {
+ if (string.IsNullOrEmpty(input)) {
+ Logging.LogNullError(nameof(input));
+ return null;
+ }
+
+ Bot bot = Bot.Bots.Values.FirstOrDefault();
+ if (bot == null) {
+ return "ERROR: No bots are enabled!";
+ }
+
+ if (Program.GlobalConfig.SteamOwnerID == 0) {
+ return "Refusing to handle request because SteamOwnerID is not set!";
+ }
+
+ string command = "!" + input;
+ string output = bot.Response(Program.GlobalConfig.SteamOwnerID, command).Result; // TODO: This should be asynchronous
+
+ Logging.LogGenericInfo("Answered to command: " + input + " with: " + output);
+ return output;
+ }
+
+ public void Dispose() {
+ if (ServiceHost != null) {
+ ServiceHost.Close();
+ }
+
+ if (Client != null) {
+ Client.Close();
+ }
+ }
+
internal bool IsServerRunning() => ServiceHost != null;
internal void StartServer() {
@@ -98,28 +130,6 @@ namespace ArchiSteamFarm {
return Client.HandleCommand(input);
}
-
- public string HandleCommand(string input) {
- if (string.IsNullOrEmpty(input)) {
- Logging.LogNullError(nameof(input));
- return null;
- }
-
- Bot bot = Bot.Bots.Values.FirstOrDefault();
- if (bot == null) {
- return "ERROR: No bots are enabled!";
- }
-
- if (Program.GlobalConfig.SteamOwnerID == 0) {
- return "Refusing to handle request because SteamOwnerID is not set!";
- }
-
- string command = "!" + input;
- string output = bot.Response(Program.GlobalConfig.SteamOwnerID, command).Result; // TODO: This should be asynchronous
-
- Logging.LogGenericInfo("Answered to command: " + input + " with: " + output);
- return output;
- }
}
internal sealed class Client : ClientBase, IWCF {
diff --git a/ArchiSteamFarm/config/ASF.json b/ArchiSteamFarm/config/ASF.json
index fc2e93d90..3354c3d6d 100644
--- a/ArchiSteamFarm/config/ASF.json
+++ b/ArchiSteamFarm/config/ASF.json
@@ -18,7 +18,6 @@
"WCFHostname": "localhost",
"WCFPort": 1242,
"Statistics": true,
- "HackIgnoreMachineID": false,
"Blacklist": [
267420,
303700,