async/dispose code review

This commit is contained in:
JustArchi
2017-08-04 19:26:37 +02:00
parent 6f80ee9faa
commit 6aa5a633e4
9 changed files with 180 additions and 172 deletions

View File

@@ -86,6 +86,7 @@ namespace ArchiSteamFarm {
PublicInventorySemaphore.Dispose(); PublicInventorySemaphore.Dispose();
SessionSemaphore.Dispose(); SessionSemaphore.Dispose();
TradeTokenSemaphore.Dispose(); TradeTokenSemaphore.Dispose();
WebBrowser.Dispose();
} }
internal async Task<bool> AcceptTradeOffer(ulong tradeID) { internal async Task<bool> AcceptTradeOffer(ulong tradeID) {

View File

@@ -231,22 +231,24 @@ namespace ArchiSteamFarm {
public void Dispose() { public void Dispose() {
// Those are objects that are always being created if constructor doesn't throw exception // Those are objects that are always being created if constructor doesn't throw exception
ArchiWebHandler.Dispose();
CallbackSemaphore.Dispose(); CallbackSemaphore.Dispose();
CardsFarmer.Dispose();
HeartBeatTimer.Dispose();
InitializationSemaphore.Dispose(); InitializationSemaphore.Dispose();
LootingSemaphore.Dispose(); LootingSemaphore.Dispose();
PICSSemaphore.Dispose(); PICSSemaphore.Dispose();
Trading.Dispose();
// Those are objects that might be null and the check should be in-place // Those are objects that might be null and the check should be in-place
ArchiWebHandler?.Dispose();
BotDatabase?.Dispose();
CardsFarmer?.Dispose();
CardsFarmerResumeTimer?.Dispose(); CardsFarmerResumeTimer?.Dispose();
ConnectionFailureTimer?.Dispose(); ConnectionFailureTimer?.Dispose();
FamilySharingInactivityTimer?.Dispose(); FamilySharingInactivityTimer?.Dispose();
HeartBeatTimer?.Dispose();
PlayingWasBlockedTimer?.Dispose();
SendItemsTimer?.Dispose(); SendItemsTimer?.Dispose();
Statistics?.Dispose(); Statistics?.Dispose();
SteamSaleEvent?.Dispose(); SteamSaleEvent?.Dispose();
Trading?.Dispose();
} }
internal async Task<bool> AcceptConfirmations(bool accept, Steam.ConfirmationDetails.EType acceptedType = Steam.ConfirmationDetails.EType.Unknown, ulong acceptedSteamID = 0, HashSet<ulong> acceptedTradeIDs = null) { internal async Task<bool> AcceptConfirmations(bool accept, Steam.ConfirmationDetails.EType acceptedType = Steam.ConfirmationDetails.EType.Unknown, ulong acceptedSteamID = 0, HashSet<ulong> acceptedTradeIDs = null) {
@@ -803,13 +805,13 @@ namespace ArchiSteamFarm {
return await ResponseBlacklistAdd(steamID, args[1], args[2]).ConfigureAwait(false); return await ResponseBlacklistAdd(steamID, args[1], args[2]).ConfigureAwait(false);
} }
return ResponseBlacklistAdd(steamID, args[1]); return await ResponseBlacklistAdd(steamID, args[1]).ConfigureAwait(false);
case "!BLRM": case "!BLRM":
if (args.Length > 2) { if (args.Length > 2) {
return await ResponseBlacklistRemove(steamID, args[1], args[2]).ConfigureAwait(false); return await ResponseBlacklistRemove(steamID, args[1], args[2]).ConfigureAwait(false);
} }
return ResponseBlacklistRemove(steamID, args[1]); return await ResponseBlacklistRemove(steamID, args[1]).ConfigureAwait(false);
case "!FARM": case "!FARM":
return await ResponseFarm(steamID, args[1]).ConfigureAwait(false); return await ResponseFarm(steamID, args[1]).ConfigureAwait(false);
case "!INPUT": case "!INPUT":
@@ -825,13 +827,13 @@ namespace ArchiSteamFarm {
return await ResponseIdleQueueAdd(steamID, args[1], args[2]).ConfigureAwait(false); return await ResponseIdleQueueAdd(steamID, args[1], args[2]).ConfigureAwait(false);
} }
return ResponseIdleQueueAdd(steamID, args[1]); return await ResponseIdleQueueAdd(steamID, args[1]).ConfigureAwait(false);
case "!IQRM": case "!IQRM":
if (args.Length > 2) { if (args.Length > 2) {
return await ResponseIdleQueueRemove(steamID, args[1], args[2]).ConfigureAwait(false); return await ResponseIdleQueueRemove(steamID, args[1], args[2]).ConfigureAwait(false);
} }
return ResponseIdleQueueRemove(steamID, args[1]); return await ResponseIdleQueueRemove(steamID, args[1]).ConfigureAwait(false);
case "!LOOT": case "!LOOT":
return await ResponseLoot(steamID, args[1]).ConfigureAwait(false); return await ResponseLoot(steamID, args[1]).ConfigureAwait(false);
case "!LOOT^": case "!LOOT^":
@@ -1156,7 +1158,7 @@ namespace ArchiSteamFarm {
} }
} }
private void ImportAuthenticator(string maFilePath) { private async Task ImportAuthenticator(string maFilePath) {
if (HasMobileAuthenticator || !File.Exists(maFilePath)) { if (HasMobileAuthenticator || !File.Exists(maFilePath)) {
return; return;
} }
@@ -1164,7 +1166,8 @@ namespace ArchiSteamFarm {
ArchiLogger.LogGenericInfo(Strings.BotAuthenticatorConverting); ArchiLogger.LogGenericInfo(Strings.BotAuthenticatorConverting);
try { try {
BotDatabase.MobileAuthenticator = JsonConvert.DeserializeObject<MobileAuthenticator>(File.ReadAllText(maFilePath)); MobileAuthenticator authenticator = JsonConvert.DeserializeObject<MobileAuthenticator>(File.ReadAllText(maFilePath));
await BotDatabase.SetMobileAuthenticator(authenticator).ConfigureAwait(false);
File.Delete(maFilePath); File.Delete(maFilePath);
} catch (Exception e) { } catch (Exception e) {
ArchiLogger.LogGenericException(e); ArchiLogger.LogGenericException(e);
@@ -1183,14 +1186,14 @@ namespace ArchiSteamFarm {
if (string.IsNullOrEmpty(DeviceID)) { if (string.IsNullOrEmpty(DeviceID)) {
string deviceID = Program.GetUserInput(ASF.EUserInputType.DeviceID, BotName); string deviceID = Program.GetUserInput(ASF.EUserInputType.DeviceID, BotName);
if (string.IsNullOrEmpty(deviceID)) { if (string.IsNullOrEmpty(deviceID)) {
BotDatabase.MobileAuthenticator = null; await BotDatabase.SetMobileAuthenticator().ConfigureAwait(false);
return; return;
} }
SetUserInput(ASF.EUserInputType.DeviceID, deviceID); SetUserInput(ASF.EUserInputType.DeviceID, deviceID);
} }
BotDatabase.CorrectMobileAuthenticatorDeviceID(DeviceID); await BotDatabase.CorrectMobileAuthenticatorDeviceID(DeviceID).ConfigureAwait(false);
} }
ArchiLogger.LogGenericInfo(Strings.BotAuthenticatorImportFinished); ArchiLogger.LogGenericInfo(Strings.BotAuthenticatorImportFinished);
@@ -1584,7 +1587,7 @@ namespace ArchiSteamFarm {
goto case EResult.RateLimitExceeded; goto case EResult.RateLimitExceeded;
} }
BotDatabase.LoginKey = null; await BotDatabase.SetLoginKey().ConfigureAwait(false);
ArchiLogger.LogGenericInfo(Strings.BotRemovedExpiredLoginKey); ArchiLogger.LogGenericInfo(Strings.BotRemovedExpiredLoginKey);
break; break;
case EResult.NoConnection: case EResult.NoConnection:
@@ -1821,15 +1824,15 @@ namespace ArchiSteamFarm {
ArchiLogger.LogGenericWarning(Strings.BotAccountLocked); ArchiLogger.LogGenericWarning(Strings.BotAccountLocked);
} }
if (callback.CellID != 0) { if ((callback.CellID != 0) && (callback.CellID != Program.GlobalDatabase.CellID)) {
Program.GlobalDatabase.CellID = callback.CellID; await Program.GlobalDatabase.SetCellID(callback.CellID).ConfigureAwait(false);
} }
if (!HasMobileAuthenticator) { if (!HasMobileAuthenticator) {
// Support and convert 2FA files // Support and convert 2FA files
string maFilePath = Path.Combine(SharedInfo.ConfigDirectory, callback.ClientSteamID.ConvertToUInt64() + ".maFile"); string maFilePath = Path.Combine(SharedInfo.ConfigDirectory, callback.ClientSteamID.ConvertToUInt64() + ".maFile");
if (File.Exists(maFilePath)) { if (File.Exists(maFilePath)) {
ImportAuthenticator(maFilePath); await ImportAuthenticator(maFilePath).ConfigureAwait(false);
} }
} }
@@ -1896,7 +1899,7 @@ namespace ArchiSteamFarm {
} }
} }
private void OnLoginKey(SteamUser.LoginKeyCallback callback) { private async void OnLoginKey(SteamUser.LoginKeyCallback callback) {
if (string.IsNullOrEmpty(callback?.LoginKey)) { if (string.IsNullOrEmpty(callback?.LoginKey)) {
ArchiLogger.LogNullError(nameof(callback) + " || " + nameof(callback.LoginKey)); ArchiLogger.LogNullError(nameof(callback) + " || " + nameof(callback.LoginKey));
return; return;
@@ -1907,7 +1910,7 @@ namespace ArchiSteamFarm {
loginKey = CryptoHelper.Encrypt(BotConfig.PasswordFormat, loginKey); loginKey = CryptoHelper.Encrypt(BotConfig.PasswordFormat, loginKey);
} }
BotDatabase.LoginKey = loginKey; await BotDatabase.SetLoginKey(loginKey).ConfigureAwait(false);
SteamUser.AcceptNewLoginKey(callback); SteamUser.AcceptNewLoginKey(callback);
} }
@@ -2431,7 +2434,7 @@ namespace ArchiSteamFarm {
return null; return null;
} }
private string ResponseBlacklistAdd(ulong steamID, string targetsText) { private async Task<string> ResponseBlacklistAdd(ulong steamID, string targetsText) {
if ((steamID == 0) || string.IsNullOrEmpty(targetsText)) { if ((steamID == 0) || string.IsNullOrEmpty(targetsText)) {
ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(targetsText)); ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(targetsText));
return null; return null;
@@ -2456,7 +2459,7 @@ namespace ArchiSteamFarm {
return FormatBotResponse(string.Format(Strings.ErrorIsEmpty, nameof(targetIDs))); return FormatBotResponse(string.Format(Strings.ErrorIsEmpty, nameof(targetIDs)));
} }
BotDatabase.AddBlacklistedFromTradesSteamIDs(targetIDs); await BotDatabase.AddBlacklistedFromTradesSteamIDs(targetIDs).ConfigureAwait(false);
return FormatBotResponse(Strings.Done); return FormatBotResponse(Strings.Done);
} }
@@ -2471,7 +2474,7 @@ namespace ArchiSteamFarm {
return IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; return IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null;
} }
IEnumerable<Task<string>> tasks = bots.Select(bot => Task.Run(() => bot.ResponseBlacklistAdd(steamID, targetsText))); IEnumerable<Task<string>> tasks = bots.Select(bot => bot.ResponseBlacklistAdd(steamID, targetsText));
ICollection<string> results; ICollection<string> results;
switch (Program.GlobalConfig.OptimizationMode) { switch (Program.GlobalConfig.OptimizationMode) {
@@ -2502,7 +2505,7 @@ namespace ArchiSteamFarm {
return IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; return IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null;
} }
IEnumerable<Task<string>> tasks = bots.Select(bot => Task.Run(() => bot.ResponseBlacklistRemove(steamID, targetsText))); IEnumerable<Task<string>> tasks = bots.Select(bot => bot.ResponseBlacklistRemove(steamID, targetsText));
ICollection<string> results; ICollection<string> results;
switch (Program.GlobalConfig.OptimizationMode) { switch (Program.GlobalConfig.OptimizationMode) {
@@ -2522,7 +2525,7 @@ namespace ArchiSteamFarm {
return responses.Count > 0 ? string.Join("", responses) : null; return responses.Count > 0 ? string.Join("", responses) : null;
} }
private string ResponseBlacklistRemove(ulong steamID, string targetsText) { private async Task<string> ResponseBlacklistRemove(ulong steamID, string targetsText) {
if ((steamID == 0) || string.IsNullOrEmpty(targetsText)) { if ((steamID == 0) || string.IsNullOrEmpty(targetsText)) {
ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(targetsText)); ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(targetsText));
return null; return null;
@@ -2547,7 +2550,7 @@ namespace ArchiSteamFarm {
return FormatBotResponse(string.Format(Strings.ErrorIsEmpty, nameof(targetIDs))); return FormatBotResponse(string.Format(Strings.ErrorIsEmpty, nameof(targetIDs)));
} }
BotDatabase.RemoveBlacklistedFromTradesSteamIDs(targetIDs); await BotDatabase.RemoveBlacklistedFromTradesSteamIDs(targetIDs).ConfigureAwait(false);
return FormatBotResponse(Strings.Done); return FormatBotResponse(Strings.Done);
} }
@@ -2671,7 +2674,7 @@ namespace ArchiSteamFarm {
return result; return result;
} }
private string ResponseIdleQueueAdd(ulong steamID, string targetsText) { private async Task<string> ResponseIdleQueueAdd(ulong steamID, string targetsText) {
if ((steamID == 0) || string.IsNullOrEmpty(targetsText)) { if ((steamID == 0) || string.IsNullOrEmpty(targetsText)) {
ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(targetsText)); ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(targetsText));
return null; return null;
@@ -2696,7 +2699,7 @@ namespace ArchiSteamFarm {
return FormatBotResponse(string.Format(Strings.ErrorIsEmpty, nameof(appIDs))); return FormatBotResponse(string.Format(Strings.ErrorIsEmpty, nameof(appIDs)));
} }
BotDatabase.AddIdlingPriorityAppIDs(appIDs); await BotDatabase.AddIdlingPriorityAppIDs(appIDs).ConfigureAwait(false);
return FormatBotResponse(Strings.Done); return FormatBotResponse(Strings.Done);
} }
@@ -2711,7 +2714,7 @@ namespace ArchiSteamFarm {
return IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; return IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null;
} }
IEnumerable<Task<string>> tasks = bots.Select(bot => Task.Run(() => bot.ResponseIdleQueueAdd(steamID, targetsText))); IEnumerable<Task<string>> tasks = bots.Select(bot => bot.ResponseIdleQueueAdd(steamID, targetsText));
ICollection<string> results; ICollection<string> results;
switch (Program.GlobalConfig.OptimizationMode) { switch (Program.GlobalConfig.OptimizationMode) {
@@ -2742,7 +2745,7 @@ namespace ArchiSteamFarm {
return IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null; return IsOwner(steamID) ? FormatStaticResponse(string.Format(Strings.BotNotFound, botNames)) : null;
} }
IEnumerable<Task<string>> tasks = bots.Select(bot => Task.Run(() => bot.ResponseIdleQueueRemove(steamID, targetsText))); IEnumerable<Task<string>> tasks = bots.Select(bot => bot.ResponseIdleQueueRemove(steamID, targetsText));
ICollection<string> results; ICollection<string> results;
switch (Program.GlobalConfig.OptimizationMode) { switch (Program.GlobalConfig.OptimizationMode) {
@@ -2762,7 +2765,7 @@ namespace ArchiSteamFarm {
return responses.Count > 0 ? string.Join("", responses) : null; return responses.Count > 0 ? string.Join("", responses) : null;
} }
private string ResponseIdleQueueRemove(ulong steamID, string targetsText) { private async Task<string> ResponseIdleQueueRemove(ulong steamID, string targetsText) {
if ((steamID == 0) || string.IsNullOrEmpty(targetsText)) { if ((steamID == 0) || string.IsNullOrEmpty(targetsText)) {
ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(targetsText)); ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(targetsText));
return null; return null;
@@ -2787,7 +2790,7 @@ namespace ArchiSteamFarm {
return FormatBotResponse(string.Format(Strings.ErrorIsEmpty, nameof(appIDs))); return FormatBotResponse(string.Format(Strings.ErrorIsEmpty, nameof(appIDs)));
} }
BotDatabase.RemoveIdlingPriorityAppIDs(appIDs); await BotDatabase.RemoveIdlingPriorityAppIDs(appIDs).ConfigureAwait(false);
return FormatBotResponse(Strings.Done); return FormatBotResponse(Strings.Done);
} }
@@ -4104,7 +4107,7 @@ namespace ArchiSteamFarm {
if (!HasMobileAuthenticator) { if (!HasMobileAuthenticator) {
string maFilePath = BotPath + ".maFile"; string maFilePath = BotPath + ".maFile";
if (File.Exists(maFilePath)) { if (File.Exists(maFilePath)) {
ImportAuthenticator(maFilePath); await ImportAuthenticator(maFilePath).ConfigureAwait(false);
} }
} }

View File

@@ -26,49 +26,25 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.IO; using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Newtonsoft.Json; using Newtonsoft.Json;
namespace ArchiSteamFarm { namespace ArchiSteamFarm {
internal sealed class BotDatabase { internal sealed class BotDatabase : IDisposable {
[JsonProperty(Required = Required.DisallowNull)] [JsonProperty(Required = Required.DisallowNull)]
private readonly ConcurrentHashSet<ulong> BlacklistedFromTradesSteamIDs = new ConcurrentHashSet<ulong>(); private readonly ConcurrentHashSet<ulong> BlacklistedFromTradesSteamIDs = new ConcurrentHashSet<ulong>();
private readonly object FileLock = new object(); private readonly SemaphoreSlim FileSemaphore = new SemaphoreSlim(1, 1);
[JsonProperty(Required = Required.DisallowNull)] [JsonProperty(Required = Required.DisallowNull)]
private readonly ConcurrentHashSet<uint> IdlingPriorityAppIDs = new ConcurrentHashSet<uint>(); private readonly ConcurrentHashSet<uint> IdlingPriorityAppIDs = new ConcurrentHashSet<uint>();
internal string LoginKey { [JsonProperty(PropertyName = "_LoginKey")]
get => _LoginKey; internal string LoginKey { get; private set; }
set { [JsonProperty(PropertyName = "_MobileAuthenticator")]
if (_LoginKey == value) { internal MobileAuthenticator MobileAuthenticator { get; private set; }
return;
}
_LoginKey = value;
Save();
}
}
internal MobileAuthenticator MobileAuthenticator {
get => _MobileAuthenticator;
set {
if (_MobileAuthenticator == value) {
return;
}
_MobileAuthenticator = value;
Save();
}
}
[JsonProperty]
private string _LoginKey;
[JsonProperty]
private MobileAuthenticator _MobileAuthenticator;
private string FilePath; private string FilePath;
@@ -79,43 +55,51 @@ namespace ArchiSteamFarm {
} }
FilePath = filePath; FilePath = filePath;
Save(); Save().Wait();
} }
// This constructor is used only by deserializer // This constructor is used only by deserializer
[SuppressMessage("ReSharper", "UnusedMember.Local")] [SuppressMessage("ReSharper", "UnusedMember.Local")]
private BotDatabase() { } private BotDatabase() { }
internal void AddBlacklistedFromTradesSteamIDs(HashSet<ulong> steamIDs) { public void Dispose() {
// Those are objects that are always being created if constructor doesn't throw exception
FileSemaphore.Dispose();
// Those are objects that might be null and the check should be in-place
MobileAuthenticator?.Dispose();
}
internal async Task AddBlacklistedFromTradesSteamIDs(HashSet<ulong> steamIDs) {
if ((steamIDs == null) || (steamIDs.Count == 0)) { if ((steamIDs == null) || (steamIDs.Count == 0)) {
ASF.ArchiLogger.LogNullError(nameof(steamIDs)); ASF.ArchiLogger.LogNullError(nameof(steamIDs));
return; return;
} }
if (BlacklistedFromTradesSteamIDs.AddRange(steamIDs)) { if (BlacklistedFromTradesSteamIDs.AddRange(steamIDs)) {
Save(); await Save().ConfigureAwait(false);
} }
} }
internal void AddIdlingPriorityAppIDs(HashSet<uint> appIDs) { internal async Task AddIdlingPriorityAppIDs(HashSet<uint> appIDs) {
if ((appIDs == null) || (appIDs.Count == 0)) { if ((appIDs == null) || (appIDs.Count == 0)) {
ASF.ArchiLogger.LogNullError(nameof(appIDs)); ASF.ArchiLogger.LogNullError(nameof(appIDs));
return; return;
} }
if (IdlingPriorityAppIDs.AddRange(appIDs)) { if (IdlingPriorityAppIDs.AddRange(appIDs)) {
Save(); await Save().ConfigureAwait(false);
} }
} }
internal void CorrectMobileAuthenticatorDeviceID(string deviceID) { internal async Task CorrectMobileAuthenticatorDeviceID(string deviceID) {
if (string.IsNullOrEmpty(deviceID) || (MobileAuthenticator == null)) { if (string.IsNullOrEmpty(deviceID) || (MobileAuthenticator == null)) {
ASF.ArchiLogger.LogNullError(nameof(deviceID) + " || " + nameof(MobileAuthenticator)); ASF.ArchiLogger.LogNullError(nameof(deviceID) + " || " + nameof(MobileAuthenticator));
return; return;
} }
if (MobileAuthenticator.CorrectDeviceID(deviceID)) { if (MobileAuthenticator.CorrectDeviceID(deviceID)) {
Save(); await Save().ConfigureAwait(false);
} }
} }
@@ -170,49 +154,69 @@ namespace ArchiSteamFarm {
return botDatabase; return botDatabase;
} }
internal void RemoveBlacklistedFromTradesSteamIDs(HashSet<ulong> steamIDs) { internal async Task RemoveBlacklistedFromTradesSteamIDs(HashSet<ulong> steamIDs) {
if ((steamIDs == null) || (steamIDs.Count == 0)) { if ((steamIDs == null) || (steamIDs.Count == 0)) {
ASF.ArchiLogger.LogNullError(nameof(steamIDs)); ASF.ArchiLogger.LogNullError(nameof(steamIDs));
return; return;
} }
if (BlacklistedFromTradesSteamIDs.RemoveRange(steamIDs)) { if (BlacklistedFromTradesSteamIDs.RemoveRange(steamIDs)) {
Save(); await Save().ConfigureAwait(false);
} }
} }
internal void RemoveIdlingPriorityAppIDs(HashSet<uint> appIDs) { internal async Task RemoveIdlingPriorityAppIDs(HashSet<uint> appIDs) {
if ((appIDs == null) || (appIDs.Count == 0)) { if ((appIDs == null) || (appIDs.Count == 0)) {
ASF.ArchiLogger.LogNullError(nameof(appIDs)); ASF.ArchiLogger.LogNullError(nameof(appIDs));
return; return;
} }
if (IdlingPriorityAppIDs.RemoveRange(appIDs)) { if (IdlingPriorityAppIDs.RemoveRange(appIDs)) {
Save(); await Save().ConfigureAwait(false);
} }
} }
private void Save() { internal async Task SetLoginKey(string value = null) {
if (value == LoginKey) {
return;
}
LoginKey = value;
await Save().ConfigureAwait(false);
}
internal async Task SetMobileAuthenticator(MobileAuthenticator value = null) {
if (value == MobileAuthenticator) {
return;
}
MobileAuthenticator = value;
await Save().ConfigureAwait(false);
}
private async Task Save() {
string json = JsonConvert.SerializeObject(this); string json = JsonConvert.SerializeObject(this);
if (string.IsNullOrEmpty(json)) { if (string.IsNullOrEmpty(json)) {
ASF.ArchiLogger.LogNullError(nameof(json)); ASF.ArchiLogger.LogNullError(nameof(json));
return; return;
} }
lock (FileLock) { string newFilePath = FilePath + ".new";
string newFilePath = FilePath + ".new";
try { await FileSemaphore.WaitAsync().ConfigureAwait(false);
File.WriteAllText(newFilePath, json);
if (File.Exists(FilePath)) { try {
File.Replace(newFilePath, FilePath, null); await File.WriteAllTextAsync(newFilePath, json).ConfigureAwait(false);
} else {
File.Move(newFilePath, FilePath); if (File.Exists(FilePath)) {
} File.Replace(newFilePath, FilePath, null);
} catch (Exception e) { } else {
ASF.ArchiLogger.LogGenericException(e); File.Move(newFilePath, FilePath);
} }
} catch (Exception e) {
ASF.ArchiLogger.LogGenericException(e);
} finally {
FileSemaphore.Release();
} }
} }
} }

View File

@@ -89,6 +89,7 @@ namespace ArchiSteamFarm {
EventSemaphore.Dispose(); EventSemaphore.Dispose();
FarmingInitializationSemaphore.Dispose(); FarmingInitializationSemaphore.Dispose();
FarmingResetSemaphore.Dispose(); FarmingResetSemaphore.Dispose();
GamesToFarm.Dispose();
// Those are objects that might be null and the check should be in-place // Those are objects that might be null and the check should be in-place
IdleFarmingTimer?.Dispose(); IdleFarmingTimer?.Dispose();

View File

@@ -31,12 +31,12 @@ namespace ArchiSteamFarm {
internal sealed class ConcurrentSortedHashSet<T> : IDisposable, IReadOnlyCollection<T>, ISet<T> { internal sealed class ConcurrentSortedHashSet<T> : IDisposable, IReadOnlyCollection<T>, ISet<T> {
public int Count { public int Count {
get { get {
SemaphoreSlim.Wait(); CollectionSemaphore.Wait();
try { try {
return BackingCollection.Count; return BackingCollection.Count;
} finally { } finally {
SemaphoreSlim.Release(); CollectionSemaphore.Release();
} }
} }
} }
@@ -44,159 +44,159 @@ namespace ArchiSteamFarm {
public bool IsReadOnly => false; public bool IsReadOnly => false;
private readonly HashSet<T> BackingCollection = new HashSet<T>(); private readonly HashSet<T> BackingCollection = new HashSet<T>();
private readonly SemaphoreSlim SemaphoreSlim = new SemaphoreSlim(1, 1); private readonly SemaphoreSlim CollectionSemaphore = new SemaphoreSlim(1, 1);
public bool Add(T item) { public bool Add(T item) {
SemaphoreSlim.Wait(); CollectionSemaphore.Wait();
try { try {
return BackingCollection.Add(item); return BackingCollection.Add(item);
} finally { } finally {
SemaphoreSlim.Release(); CollectionSemaphore.Release();
} }
} }
public void Clear() { public void Clear() {
SemaphoreSlim.Wait(); CollectionSemaphore.Wait();
try { try {
BackingCollection.Clear(); BackingCollection.Clear();
} finally { } finally {
SemaphoreSlim.Release(); CollectionSemaphore.Release();
} }
} }
public bool Contains(T item) { public bool Contains(T item) {
SemaphoreSlim.Wait(); CollectionSemaphore.Wait();
try { try {
return BackingCollection.Contains(item); return BackingCollection.Contains(item);
} finally { } finally {
SemaphoreSlim.Release(); CollectionSemaphore.Release();
} }
} }
public void CopyTo(T[] array, int arrayIndex) { public void CopyTo(T[] array, int arrayIndex) {
SemaphoreSlim.Wait(); CollectionSemaphore.Wait();
try { try {
BackingCollection.CopyTo(array, arrayIndex); BackingCollection.CopyTo(array, arrayIndex);
} finally { } finally {
SemaphoreSlim.Release(); CollectionSemaphore.Release();
} }
} }
public void Dispose() => SemaphoreSlim.Dispose(); public void Dispose() => CollectionSemaphore.Dispose();
public void ExceptWith(IEnumerable<T> other) { public void ExceptWith(IEnumerable<T> other) {
SemaphoreSlim.Wait(); CollectionSemaphore.Wait();
try { try {
BackingCollection.ExceptWith(other); BackingCollection.ExceptWith(other);
} finally { } finally {
SemaphoreSlim.Release(); CollectionSemaphore.Release();
} }
} }
public IEnumerator<T> GetEnumerator() => new ConcurrentEnumerator<T>(BackingCollection, SemaphoreSlim); public IEnumerator<T> GetEnumerator() => new ConcurrentEnumerator<T>(BackingCollection, CollectionSemaphore);
public void IntersectWith(IEnumerable<T> other) { public void IntersectWith(IEnumerable<T> other) {
SemaphoreSlim.Wait(); CollectionSemaphore.Wait();
try { try {
BackingCollection.IntersectWith(other); BackingCollection.IntersectWith(other);
} finally { } finally {
SemaphoreSlim.Release(); CollectionSemaphore.Release();
} }
} }
public bool IsProperSubsetOf(IEnumerable<T> other) { public bool IsProperSubsetOf(IEnumerable<T> other) {
SemaphoreSlim.Wait(); CollectionSemaphore.Wait();
try { try {
return BackingCollection.IsProperSubsetOf(other); return BackingCollection.IsProperSubsetOf(other);
} finally { } finally {
SemaphoreSlim.Release(); CollectionSemaphore.Release();
} }
} }
public bool IsProperSupersetOf(IEnumerable<T> other) { public bool IsProperSupersetOf(IEnumerable<T> other) {
SemaphoreSlim.Wait(); CollectionSemaphore.Wait();
try { try {
return BackingCollection.IsProperSupersetOf(other); return BackingCollection.IsProperSupersetOf(other);
} finally { } finally {
SemaphoreSlim.Release(); CollectionSemaphore.Release();
} }
} }
public bool IsSubsetOf(IEnumerable<T> other) { public bool IsSubsetOf(IEnumerable<T> other) {
SemaphoreSlim.Wait(); CollectionSemaphore.Wait();
try { try {
return BackingCollection.IsSubsetOf(other); return BackingCollection.IsSubsetOf(other);
} finally { } finally {
SemaphoreSlim.Release(); CollectionSemaphore.Release();
} }
} }
public bool IsSupersetOf(IEnumerable<T> other) { public bool IsSupersetOf(IEnumerable<T> other) {
SemaphoreSlim.Wait(); CollectionSemaphore.Wait();
try { try {
return BackingCollection.IsSupersetOf(other); return BackingCollection.IsSupersetOf(other);
} finally { } finally {
SemaphoreSlim.Release(); CollectionSemaphore.Release();
} }
} }
public bool Overlaps(IEnumerable<T> other) { public bool Overlaps(IEnumerable<T> other) {
SemaphoreSlim.Wait(); CollectionSemaphore.Wait();
try { try {
return BackingCollection.Overlaps(other); return BackingCollection.Overlaps(other);
} finally { } finally {
SemaphoreSlim.Release(); CollectionSemaphore.Release();
} }
} }
public bool Remove(T item) { public bool Remove(T item) {
SemaphoreSlim.Wait(); CollectionSemaphore.Wait();
try { try {
return BackingCollection.Remove(item); return BackingCollection.Remove(item);
} finally { } finally {
SemaphoreSlim.Release(); CollectionSemaphore.Release();
} }
} }
public bool SetEquals(IEnumerable<T> other) { public bool SetEquals(IEnumerable<T> other) {
SemaphoreSlim.Wait(); CollectionSemaphore.Wait();
try { try {
return BackingCollection.SetEquals(other); return BackingCollection.SetEquals(other);
} finally { } finally {
SemaphoreSlim.Release(); CollectionSemaphore.Release();
} }
} }
public void SymmetricExceptWith(IEnumerable<T> other) { public void SymmetricExceptWith(IEnumerable<T> other) {
SemaphoreSlim.Wait(); CollectionSemaphore.Wait();
try { try {
BackingCollection.SymmetricExceptWith(other); BackingCollection.SymmetricExceptWith(other);
} finally { } finally {
SemaphoreSlim.Release(); CollectionSemaphore.Release();
} }
} }
public void UnionWith(IEnumerable<T> other) { public void UnionWith(IEnumerable<T> other) {
SemaphoreSlim.Wait(); CollectionSemaphore.Wait();
try { try {
BackingCollection.UnionWith(other); BackingCollection.UnionWith(other);
} finally { } finally {
SemaphoreSlim.Release(); CollectionSemaphore.Release();
} }
} }
@@ -204,7 +204,7 @@ namespace ArchiSteamFarm {
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
internal void ReplaceWith(IEnumerable<T> other) { internal void ReplaceWith(IEnumerable<T> other) {
SemaphoreSlim.Wait(); CollectionSemaphore.Wait();
try { try {
BackingCollection.Clear(); BackingCollection.Clear();
@@ -213,7 +213,7 @@ namespace ArchiSteamFarm {
BackingCollection.Add(item); BackingCollection.Add(item);
} }
} finally { } finally {
SemaphoreSlim.Release(); CollectionSemaphore.Release();
} }
} }
} }

View File

@@ -42,24 +42,11 @@ namespace ArchiSteamFarm {
[JsonProperty(Required = Required.DisallowNull)] [JsonProperty(Required = Required.DisallowNull)]
internal readonly InMemoryServerListProvider ServerListProvider = new InMemoryServerListProvider(); internal readonly InMemoryServerListProvider ServerListProvider = new InMemoryServerListProvider();
private readonly object FileLock = new object(); private readonly SemaphoreSlim FileSemaphore = new SemaphoreSlim(1, 1);
private readonly SemaphoreSlim PackagesRefreshSemaphore = new SemaphoreSlim(1, 1); private readonly SemaphoreSlim PackagesRefreshSemaphore = new SemaphoreSlim(1, 1);
internal uint CellID { [JsonProperty(PropertyName = "_CellID", Required = Required.DisallowNull)]
get => _CellID; internal uint CellID { get; private set; }
set {
if ((value == 0) || (_CellID == value)) {
return;
}
_CellID = value;
Save();
}
}
[JsonProperty(Required = Required.DisallowNull)]
private uint _CellID;
private string FilePath; private string FilePath;
@@ -70,13 +57,20 @@ namespace ArchiSteamFarm {
} }
FilePath = filePath; FilePath = filePath;
Save(); Save().Wait();
} }
// This constructor is used only by deserializer // This constructor is used only by deserializer
private GlobalDatabase() => ServerListProvider.ServerListUpdated += OnServerListUpdated; private GlobalDatabase() => ServerListProvider.ServerListUpdated += OnServerListUpdated;
public void Dispose() => ServerListProvider.ServerListUpdated -= OnServerListUpdated; public void Dispose() {
// Events we registered
ServerListProvider.ServerListUpdated -= OnServerListUpdated;
// Those are objects that are always being created if constructor doesn't throw exception
FileSemaphore.Dispose();
PackagesRefreshSemaphore.Dispose();
}
internal static GlobalDatabase Load(string filePath) { internal static GlobalDatabase Load(string filePath) {
if (string.IsNullOrEmpty(filePath)) { if (string.IsNullOrEmpty(filePath)) {
@@ -136,35 +130,46 @@ namespace ArchiSteamFarm {
} }
} }
Save(); await Save().ConfigureAwait(false);
} finally { } finally {
PackagesRefreshSemaphore.Release(); PackagesRefreshSemaphore.Release();
} }
} }
private void OnServerListUpdated(object sender, EventArgs e) => Save(); internal async Task SetCellID(uint value = 0) {
if (value == CellID) {
return;
}
private void Save() { CellID = value;
await Save().ConfigureAwait(false);
}
private async void OnServerListUpdated(object sender, EventArgs e) => await Save().ConfigureAwait(false);
private async Task Save() {
string json = JsonConvert.SerializeObject(this); string json = JsonConvert.SerializeObject(this);
if (string.IsNullOrEmpty(json)) { if (string.IsNullOrEmpty(json)) {
ASF.ArchiLogger.LogNullError(nameof(json)); ASF.ArchiLogger.LogNullError(nameof(json));
return; return;
} }
lock (FileLock) { string newFilePath = FilePath + ".new";
string newFilePath = FilePath + ".new";
try { await FileSemaphore.WaitAsync().ConfigureAwait(false);
File.WriteAllText(newFilePath, json);
if (File.Exists(FilePath)) { try {
File.Replace(newFilePath, FilePath, null); File.WriteAllText(newFilePath, json);
} else {
File.Move(newFilePath, FilePath); if (File.Exists(FilePath)) {
} File.Replace(newFilePath, FilePath, null);
} catch (Exception e) { } else {
ASF.ArchiLogger.LogGenericException(e); File.Move(newFilePath, FilePath);
} }
} catch (Exception e) {
ASF.ArchiLogger.LogGenericException(e);
} finally {
FileSemaphore.Release();
} }
} }
} }

View File

@@ -38,16 +38,8 @@ namespace ArchiSteamFarm {
internal readonly ProtocolTypes ProtocolTypes; internal readonly ProtocolTypes ProtocolTypes;
internal ServerRecordEndPoint(string host, ushort port, ProtocolTypes protocolTypes) { internal ServerRecordEndPoint(string host, ushort port, ProtocolTypes protocolTypes) {
if (string.IsNullOrEmpty(host)) { if (string.IsNullOrEmpty(host) || (port == 0) || (protocolTypes == 0)) {
throw new ArgumentNullException(nameof(host)); throw new ArgumentNullException(nameof(host) + " || " + nameof(port) + " || " + nameof(protocolTypes));
}
if (port == 0) {
throw new ArgumentNullException(nameof(port));
}
if (protocolTypes == 0) {
throw new ArgumentNullException(nameof(protocolTypes));
} }
Host = host; Host = host;

View File

@@ -40,7 +40,7 @@ namespace ArchiSteamFarm {
private const string URL = "https://" + SharedInfo.StatisticsServer; private const string URL = "https://" + SharedInfo.StatisticsServer;
private readonly Bot Bot; private readonly Bot Bot;
private readonly SemaphoreSlim Semaphore = new SemaphoreSlim(1, 1); private readonly SemaphoreSlim RequestsSemaphore = new SemaphoreSlim(1, 1);
private DateTime LastAnnouncementCheck = DateTime.MinValue; private DateTime LastAnnouncementCheck = DateTime.MinValue;
private DateTime LastHeartBeat = DateTime.MinValue; private DateTime LastHeartBeat = DateTime.MinValue;
@@ -49,7 +49,7 @@ namespace ArchiSteamFarm {
internal Statistics(Bot bot) => Bot = bot ?? throw new ArgumentNullException(nameof(bot)); internal Statistics(Bot bot) => Bot = bot ?? throw new ArgumentNullException(nameof(bot));
public void Dispose() => Semaphore.Dispose(); public void Dispose() => RequestsSemaphore.Dispose();
internal async Task OnHeartBeat() { internal async Task OnHeartBeat() {
// Request persona update if needed // Request persona update if needed
@@ -62,7 +62,7 @@ namespace ArchiSteamFarm {
return; return;
} }
await Semaphore.WaitAsync().ConfigureAwait(false); await RequestsSemaphore.WaitAsync().ConfigureAwait(false);
try { try {
if (!ShouldSendHeartBeats || (DateTime.UtcNow < LastHeartBeat.AddMinutes(MinHeartBeatTTL))) { if (!ShouldSendHeartBeats || (DateTime.UtcNow < LastHeartBeat.AddMinutes(MinHeartBeatTTL))) {
@@ -80,7 +80,7 @@ namespace ArchiSteamFarm {
LastHeartBeat = DateTime.UtcNow; LastHeartBeat = DateTime.UtcNow;
} }
} finally { } finally {
Semaphore.Release(); RequestsSemaphore.Release();
} }
} }
@@ -114,7 +114,7 @@ namespace ArchiSteamFarm {
} }
} }
await Semaphore.WaitAsync().ConfigureAwait(false); await RequestsSemaphore.WaitAsync().ConfigureAwait(false);
try { try {
if (DateTime.UtcNow < LastAnnouncementCheck.AddHours(MinAnnouncementCheckTTL)) { if (DateTime.UtcNow < LastAnnouncementCheck.AddHours(MinAnnouncementCheckTTL)) {
@@ -154,7 +154,7 @@ namespace ArchiSteamFarm {
ShouldSendHeartBeats = true; ShouldSendHeartBeats = true;
} }
} finally { } finally {
Semaphore.Release(); RequestsSemaphore.Release();
} }
} }
} }

View File

@@ -34,7 +34,7 @@ using Newtonsoft.Json;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
namespace ArchiSteamFarm { namespace ArchiSteamFarm {
internal sealed class WebBrowser { internal sealed class WebBrowser : IDisposable {
internal const byte MaxTries = 5; // Defines maximum number of recommended tries for a single request internal const byte MaxTries = 5; // Defines maximum number of recommended tries for a single request
private const byte ExtendedTimeoutMultiplier = 10; // Defines multiplier of timeout for WebBrowsers dealing with huge data (ASF update) private const byte ExtendedTimeoutMultiplier = 10; // Defines multiplier of timeout for WebBrowsers dealing with huge data (ASF update)
@@ -63,6 +63,8 @@ namespace ArchiSteamFarm {
HttpClient.DefaultRequestHeaders.UserAgent.ParseAdd(SharedInfo.AssemblyName + "/" + SharedInfo.Version); HttpClient.DefaultRequestHeaders.UserAgent.ParseAdd(SharedInfo.AssemblyName + "/" + SharedInfo.Version);
} }
public void Dispose() => HttpClient.Dispose();
internal static void Init() { internal static void Init() {
// Set max connection limit from default of 2 to desired value // Set max connection limit from default of 2 to desired value
ServicePointManager.DefaultConnectionLimit = MaxConnections; ServicePointManager.DefaultConnectionLimit = MaxConnections;