mirror of
https://github.com/JustArchiNET/ArchiSteamFarm
synced 2024-11-10 07:04:27 +00:00
async/dispose code review
This commit is contained in:
parent
6f80ee9faa
commit
6aa5a633e4
9 changed files with 180 additions and 172 deletions
|
@ -86,6 +86,7 @@ namespace ArchiSteamFarm {
|
|||
PublicInventorySemaphore.Dispose();
|
||||
SessionSemaphore.Dispose();
|
||||
TradeTokenSemaphore.Dispose();
|
||||
WebBrowser.Dispose();
|
||||
}
|
||||
|
||||
internal async Task<bool> AcceptTradeOffer(ulong tradeID) {
|
||||
|
|
|
@ -231,22 +231,24 @@ namespace ArchiSteamFarm {
|
|||
|
||||
public void Dispose() {
|
||||
// Those are objects that are always being created if constructor doesn't throw exception
|
||||
ArchiWebHandler.Dispose();
|
||||
CallbackSemaphore.Dispose();
|
||||
CardsFarmer.Dispose();
|
||||
HeartBeatTimer.Dispose();
|
||||
InitializationSemaphore.Dispose();
|
||||
LootingSemaphore.Dispose();
|
||||
PICSSemaphore.Dispose();
|
||||
Trading.Dispose();
|
||||
|
||||
// Those are objects that might be null and the check should be in-place
|
||||
ArchiWebHandler?.Dispose();
|
||||
BotDatabase?.Dispose();
|
||||
CardsFarmer?.Dispose();
|
||||
CardsFarmerResumeTimer?.Dispose();
|
||||
ConnectionFailureTimer?.Dispose();
|
||||
FamilySharingInactivityTimer?.Dispose();
|
||||
HeartBeatTimer?.Dispose();
|
||||
PlayingWasBlockedTimer?.Dispose();
|
||||
SendItemsTimer?.Dispose();
|
||||
Statistics?.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) {
|
||||
|
@ -803,13 +805,13 @@ namespace ArchiSteamFarm {
|
|||
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":
|
||||
if (args.Length > 2) {
|
||||
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":
|
||||
return await ResponseFarm(steamID, args[1]).ConfigureAwait(false);
|
||||
case "!INPUT":
|
||||
|
@ -825,13 +827,13 @@ namespace ArchiSteamFarm {
|
|||
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":
|
||||
if (args.Length > 2) {
|
||||
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":
|
||||
return await ResponseLoot(steamID, args[1]).ConfigureAwait(false);
|
||||
case "!LOOT^":
|
||||
|
@ -1156,7 +1158,7 @@ namespace ArchiSteamFarm {
|
|||
}
|
||||
}
|
||||
|
||||
private void ImportAuthenticator(string maFilePath) {
|
||||
private async Task ImportAuthenticator(string maFilePath) {
|
||||
if (HasMobileAuthenticator || !File.Exists(maFilePath)) {
|
||||
return;
|
||||
}
|
||||
|
@ -1164,7 +1166,8 @@ namespace ArchiSteamFarm {
|
|||
ArchiLogger.LogGenericInfo(Strings.BotAuthenticatorConverting);
|
||||
|
||||
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);
|
||||
} catch (Exception e) {
|
||||
ArchiLogger.LogGenericException(e);
|
||||
|
@ -1183,14 +1186,14 @@ namespace ArchiSteamFarm {
|
|||
if (string.IsNullOrEmpty(DeviceID)) {
|
||||
string deviceID = Program.GetUserInput(ASF.EUserInputType.DeviceID, BotName);
|
||||
if (string.IsNullOrEmpty(deviceID)) {
|
||||
BotDatabase.MobileAuthenticator = null;
|
||||
await BotDatabase.SetMobileAuthenticator().ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
|
||||
SetUserInput(ASF.EUserInputType.DeviceID, deviceID);
|
||||
}
|
||||
|
||||
BotDatabase.CorrectMobileAuthenticatorDeviceID(DeviceID);
|
||||
await BotDatabase.CorrectMobileAuthenticatorDeviceID(DeviceID).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
ArchiLogger.LogGenericInfo(Strings.BotAuthenticatorImportFinished);
|
||||
|
@ -1584,7 +1587,7 @@ namespace ArchiSteamFarm {
|
|||
goto case EResult.RateLimitExceeded;
|
||||
}
|
||||
|
||||
BotDatabase.LoginKey = null;
|
||||
await BotDatabase.SetLoginKey().ConfigureAwait(false);
|
||||
ArchiLogger.LogGenericInfo(Strings.BotRemovedExpiredLoginKey);
|
||||
break;
|
||||
case EResult.NoConnection:
|
||||
|
@ -1821,15 +1824,15 @@ namespace ArchiSteamFarm {
|
|||
ArchiLogger.LogGenericWarning(Strings.BotAccountLocked);
|
||||
}
|
||||
|
||||
if (callback.CellID != 0) {
|
||||
Program.GlobalDatabase.CellID = callback.CellID;
|
||||
if ((callback.CellID != 0) && (callback.CellID != Program.GlobalDatabase.CellID)) {
|
||||
await Program.GlobalDatabase.SetCellID(callback.CellID).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (!HasMobileAuthenticator) {
|
||||
// Support and convert 2FA files
|
||||
string maFilePath = Path.Combine(SharedInfo.ConfigDirectory, callback.ClientSteamID.ConvertToUInt64() + ".maFile");
|
||||
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)) {
|
||||
ArchiLogger.LogNullError(nameof(callback) + " || " + nameof(callback.LoginKey));
|
||||
return;
|
||||
|
@ -1907,7 +1910,7 @@ namespace ArchiSteamFarm {
|
|||
loginKey = CryptoHelper.Encrypt(BotConfig.PasswordFormat, loginKey);
|
||||
}
|
||||
|
||||
BotDatabase.LoginKey = loginKey;
|
||||
await BotDatabase.SetLoginKey(loginKey).ConfigureAwait(false);
|
||||
SteamUser.AcceptNewLoginKey(callback);
|
||||
}
|
||||
|
||||
|
@ -2431,7 +2434,7 @@ namespace ArchiSteamFarm {
|
|||
return null;
|
||||
}
|
||||
|
||||
private string ResponseBlacklistAdd(ulong steamID, string targetsText) {
|
||||
private async Task<string> ResponseBlacklistAdd(ulong steamID, string targetsText) {
|
||||
if ((steamID == 0) || string.IsNullOrEmpty(targetsText)) {
|
||||
ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(targetsText));
|
||||
return null;
|
||||
|
@ -2456,7 +2459,7 @@ namespace ArchiSteamFarm {
|
|||
return FormatBotResponse(string.Format(Strings.ErrorIsEmpty, nameof(targetIDs)));
|
||||
}
|
||||
|
||||
BotDatabase.AddBlacklistedFromTradesSteamIDs(targetIDs);
|
||||
await BotDatabase.AddBlacklistedFromTradesSteamIDs(targetIDs).ConfigureAwait(false);
|
||||
return FormatBotResponse(Strings.Done);
|
||||
}
|
||||
|
||||
|
@ -2471,7 +2474,7 @@ namespace ArchiSteamFarm {
|
|||
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;
|
||||
|
||||
switch (Program.GlobalConfig.OptimizationMode) {
|
||||
|
@ -2502,7 +2505,7 @@ namespace ArchiSteamFarm {
|
|||
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;
|
||||
|
||||
switch (Program.GlobalConfig.OptimizationMode) {
|
||||
|
@ -2522,7 +2525,7 @@ namespace ArchiSteamFarm {
|
|||
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)) {
|
||||
ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(targetsText));
|
||||
return null;
|
||||
|
@ -2547,7 +2550,7 @@ namespace ArchiSteamFarm {
|
|||
return FormatBotResponse(string.Format(Strings.ErrorIsEmpty, nameof(targetIDs)));
|
||||
}
|
||||
|
||||
BotDatabase.RemoveBlacklistedFromTradesSteamIDs(targetIDs);
|
||||
await BotDatabase.RemoveBlacklistedFromTradesSteamIDs(targetIDs).ConfigureAwait(false);
|
||||
return FormatBotResponse(Strings.Done);
|
||||
}
|
||||
|
||||
|
@ -2671,7 +2674,7 @@ namespace ArchiSteamFarm {
|
|||
return result;
|
||||
}
|
||||
|
||||
private string ResponseIdleQueueAdd(ulong steamID, string targetsText) {
|
||||
private async Task<string> ResponseIdleQueueAdd(ulong steamID, string targetsText) {
|
||||
if ((steamID == 0) || string.IsNullOrEmpty(targetsText)) {
|
||||
ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(targetsText));
|
||||
return null;
|
||||
|
@ -2696,7 +2699,7 @@ namespace ArchiSteamFarm {
|
|||
return FormatBotResponse(string.Format(Strings.ErrorIsEmpty, nameof(appIDs)));
|
||||
}
|
||||
|
||||
BotDatabase.AddIdlingPriorityAppIDs(appIDs);
|
||||
await BotDatabase.AddIdlingPriorityAppIDs(appIDs).ConfigureAwait(false);
|
||||
return FormatBotResponse(Strings.Done);
|
||||
}
|
||||
|
||||
|
@ -2711,7 +2714,7 @@ namespace ArchiSteamFarm {
|
|||
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;
|
||||
|
||||
switch (Program.GlobalConfig.OptimizationMode) {
|
||||
|
@ -2742,7 +2745,7 @@ namespace ArchiSteamFarm {
|
|||
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;
|
||||
|
||||
switch (Program.GlobalConfig.OptimizationMode) {
|
||||
|
@ -2762,7 +2765,7 @@ namespace ArchiSteamFarm {
|
|||
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)) {
|
||||
ArchiLogger.LogNullError(nameof(steamID) + " || " + nameof(targetsText));
|
||||
return null;
|
||||
|
@ -2787,7 +2790,7 @@ namespace ArchiSteamFarm {
|
|||
return FormatBotResponse(string.Format(Strings.ErrorIsEmpty, nameof(appIDs)));
|
||||
}
|
||||
|
||||
BotDatabase.RemoveIdlingPriorityAppIDs(appIDs);
|
||||
await BotDatabase.RemoveIdlingPriorityAppIDs(appIDs).ConfigureAwait(false);
|
||||
return FormatBotResponse(Strings.Done);
|
||||
}
|
||||
|
||||
|
@ -4104,7 +4107,7 @@ namespace ArchiSteamFarm {
|
|||
if (!HasMobileAuthenticator) {
|
||||
string maFilePath = BotPath + ".maFile";
|
||||
if (File.Exists(maFilePath)) {
|
||||
ImportAuthenticator(maFilePath);
|
||||
await ImportAuthenticator(maFilePath).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -26,49 +26,25 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace ArchiSteamFarm {
|
||||
internal sealed class BotDatabase {
|
||||
internal sealed class BotDatabase : IDisposable {
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
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)]
|
||||
private readonly ConcurrentHashSet<uint> IdlingPriorityAppIDs = new ConcurrentHashSet<uint>();
|
||||
|
||||
internal string LoginKey {
|
||||
get => _LoginKey;
|
||||
[JsonProperty(PropertyName = "_LoginKey")]
|
||||
internal string LoginKey { get; private set; }
|
||||
|
||||
set {
|
||||
if (_LoginKey == value) {
|
||||
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;
|
||||
[JsonProperty(PropertyName = "_MobileAuthenticator")]
|
||||
internal MobileAuthenticator MobileAuthenticator { get; private set; }
|
||||
|
||||
private string FilePath;
|
||||
|
||||
|
@ -79,43 +55,51 @@ namespace ArchiSteamFarm {
|
|||
}
|
||||
|
||||
FilePath = filePath;
|
||||
Save();
|
||||
Save().Wait();
|
||||
}
|
||||
|
||||
// This constructor is used only by deserializer
|
||||
[SuppressMessage("ReSharper", "UnusedMember.Local")]
|
||||
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)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(steamIDs));
|
||||
return;
|
||||
}
|
||||
|
||||
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)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(appIDs));
|
||||
return;
|
||||
}
|
||||
|
||||
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)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(deviceID) + " || " + nameof(MobileAuthenticator));
|
||||
return;
|
||||
}
|
||||
|
||||
if (MobileAuthenticator.CorrectDeviceID(deviceID)) {
|
||||
Save();
|
||||
await Save().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -170,49 +154,69 @@ namespace ArchiSteamFarm {
|
|||
return botDatabase;
|
||||
}
|
||||
|
||||
internal void RemoveBlacklistedFromTradesSteamIDs(HashSet<ulong> steamIDs) {
|
||||
internal async Task RemoveBlacklistedFromTradesSteamIDs(HashSet<ulong> steamIDs) {
|
||||
if ((steamIDs == null) || (steamIDs.Count == 0)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(steamIDs));
|
||||
return;
|
||||
}
|
||||
|
||||
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)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(appIDs));
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
if (string.IsNullOrEmpty(json)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(json));
|
||||
return;
|
||||
}
|
||||
|
||||
lock (FileLock) {
|
||||
string newFilePath = FilePath + ".new";
|
||||
string newFilePath = FilePath + ".new";
|
||||
|
||||
try {
|
||||
File.WriteAllText(newFilePath, json);
|
||||
await FileSemaphore.WaitAsync().ConfigureAwait(false);
|
||||
|
||||
if (File.Exists(FilePath)) {
|
||||
File.Replace(newFilePath, FilePath, null);
|
||||
} else {
|
||||
File.Move(newFilePath, FilePath);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
ASF.ArchiLogger.LogGenericException(e);
|
||||
try {
|
||||
await File.WriteAllTextAsync(newFilePath, json).ConfigureAwait(false);
|
||||
|
||||
if (File.Exists(FilePath)) {
|
||||
File.Replace(newFilePath, FilePath, null);
|
||||
} else {
|
||||
File.Move(newFilePath, FilePath);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
ASF.ArchiLogger.LogGenericException(e);
|
||||
} finally {
|
||||
FileSemaphore.Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -89,6 +89,7 @@ namespace ArchiSteamFarm {
|
|||
EventSemaphore.Dispose();
|
||||
FarmingInitializationSemaphore.Dispose();
|
||||
FarmingResetSemaphore.Dispose();
|
||||
GamesToFarm.Dispose();
|
||||
|
||||
// Those are objects that might be null and the check should be in-place
|
||||
IdleFarmingTimer?.Dispose();
|
||||
|
|
|
@ -31,12 +31,12 @@ namespace ArchiSteamFarm {
|
|||
internal sealed class ConcurrentSortedHashSet<T> : IDisposable, IReadOnlyCollection<T>, ISet<T> {
|
||||
public int Count {
|
||||
get {
|
||||
SemaphoreSlim.Wait();
|
||||
CollectionSemaphore.Wait();
|
||||
|
||||
try {
|
||||
return BackingCollection.Count;
|
||||
} finally {
|
||||
SemaphoreSlim.Release();
|
||||
CollectionSemaphore.Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -44,159 +44,159 @@ namespace ArchiSteamFarm {
|
|||
public bool IsReadOnly => false;
|
||||
|
||||
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) {
|
||||
SemaphoreSlim.Wait();
|
||||
CollectionSemaphore.Wait();
|
||||
|
||||
try {
|
||||
return BackingCollection.Add(item);
|
||||
} finally {
|
||||
SemaphoreSlim.Release();
|
||||
CollectionSemaphore.Release();
|
||||
}
|
||||
}
|
||||
|
||||
public void Clear() {
|
||||
SemaphoreSlim.Wait();
|
||||
CollectionSemaphore.Wait();
|
||||
|
||||
try {
|
||||
BackingCollection.Clear();
|
||||
} finally {
|
||||
SemaphoreSlim.Release();
|
||||
CollectionSemaphore.Release();
|
||||
}
|
||||
}
|
||||
|
||||
public bool Contains(T item) {
|
||||
SemaphoreSlim.Wait();
|
||||
CollectionSemaphore.Wait();
|
||||
|
||||
try {
|
||||
return BackingCollection.Contains(item);
|
||||
} finally {
|
||||
SemaphoreSlim.Release();
|
||||
CollectionSemaphore.Release();
|
||||
}
|
||||
}
|
||||
|
||||
public void CopyTo(T[] array, int arrayIndex) {
|
||||
SemaphoreSlim.Wait();
|
||||
CollectionSemaphore.Wait();
|
||||
|
||||
try {
|
||||
BackingCollection.CopyTo(array, arrayIndex);
|
||||
} finally {
|
||||
SemaphoreSlim.Release();
|
||||
CollectionSemaphore.Release();
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose() => SemaphoreSlim.Dispose();
|
||||
public void Dispose() => CollectionSemaphore.Dispose();
|
||||
|
||||
public void ExceptWith(IEnumerable<T> other) {
|
||||
SemaphoreSlim.Wait();
|
||||
CollectionSemaphore.Wait();
|
||||
|
||||
try {
|
||||
BackingCollection.ExceptWith(other);
|
||||
} 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) {
|
||||
SemaphoreSlim.Wait();
|
||||
CollectionSemaphore.Wait();
|
||||
|
||||
try {
|
||||
BackingCollection.IntersectWith(other);
|
||||
} finally {
|
||||
SemaphoreSlim.Release();
|
||||
CollectionSemaphore.Release();
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsProperSubsetOf(IEnumerable<T> other) {
|
||||
SemaphoreSlim.Wait();
|
||||
CollectionSemaphore.Wait();
|
||||
|
||||
try {
|
||||
return BackingCollection.IsProperSubsetOf(other);
|
||||
} finally {
|
||||
SemaphoreSlim.Release();
|
||||
CollectionSemaphore.Release();
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsProperSupersetOf(IEnumerable<T> other) {
|
||||
SemaphoreSlim.Wait();
|
||||
CollectionSemaphore.Wait();
|
||||
|
||||
try {
|
||||
return BackingCollection.IsProperSupersetOf(other);
|
||||
} finally {
|
||||
SemaphoreSlim.Release();
|
||||
CollectionSemaphore.Release();
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsSubsetOf(IEnumerable<T> other) {
|
||||
SemaphoreSlim.Wait();
|
||||
CollectionSemaphore.Wait();
|
||||
|
||||
try {
|
||||
return BackingCollection.IsSubsetOf(other);
|
||||
} finally {
|
||||
SemaphoreSlim.Release();
|
||||
CollectionSemaphore.Release();
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsSupersetOf(IEnumerable<T> other) {
|
||||
SemaphoreSlim.Wait();
|
||||
CollectionSemaphore.Wait();
|
||||
|
||||
try {
|
||||
return BackingCollection.IsSupersetOf(other);
|
||||
} finally {
|
||||
SemaphoreSlim.Release();
|
||||
CollectionSemaphore.Release();
|
||||
}
|
||||
}
|
||||
|
||||
public bool Overlaps(IEnumerable<T> other) {
|
||||
SemaphoreSlim.Wait();
|
||||
CollectionSemaphore.Wait();
|
||||
|
||||
try {
|
||||
return BackingCollection.Overlaps(other);
|
||||
} finally {
|
||||
SemaphoreSlim.Release();
|
||||
CollectionSemaphore.Release();
|
||||
}
|
||||
}
|
||||
|
||||
public bool Remove(T item) {
|
||||
SemaphoreSlim.Wait();
|
||||
CollectionSemaphore.Wait();
|
||||
|
||||
try {
|
||||
return BackingCollection.Remove(item);
|
||||
} finally {
|
||||
SemaphoreSlim.Release();
|
||||
CollectionSemaphore.Release();
|
||||
}
|
||||
}
|
||||
|
||||
public bool SetEquals(IEnumerable<T> other) {
|
||||
SemaphoreSlim.Wait();
|
||||
CollectionSemaphore.Wait();
|
||||
|
||||
try {
|
||||
return BackingCollection.SetEquals(other);
|
||||
} finally {
|
||||
SemaphoreSlim.Release();
|
||||
CollectionSemaphore.Release();
|
||||
}
|
||||
}
|
||||
|
||||
public void SymmetricExceptWith(IEnumerable<T> other) {
|
||||
SemaphoreSlim.Wait();
|
||||
CollectionSemaphore.Wait();
|
||||
|
||||
try {
|
||||
BackingCollection.SymmetricExceptWith(other);
|
||||
} finally {
|
||||
SemaphoreSlim.Release();
|
||||
CollectionSemaphore.Release();
|
||||
}
|
||||
}
|
||||
|
||||
public void UnionWith(IEnumerable<T> other) {
|
||||
SemaphoreSlim.Wait();
|
||||
CollectionSemaphore.Wait();
|
||||
|
||||
try {
|
||||
BackingCollection.UnionWith(other);
|
||||
} finally {
|
||||
SemaphoreSlim.Release();
|
||||
CollectionSemaphore.Release();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -204,7 +204,7 @@ namespace ArchiSteamFarm {
|
|||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
|
||||
internal void ReplaceWith(IEnumerable<T> other) {
|
||||
SemaphoreSlim.Wait();
|
||||
CollectionSemaphore.Wait();
|
||||
|
||||
try {
|
||||
BackingCollection.Clear();
|
||||
|
@ -213,7 +213,7 @@ namespace ArchiSteamFarm {
|
|||
BackingCollection.Add(item);
|
||||
}
|
||||
} finally {
|
||||
SemaphoreSlim.Release();
|
||||
CollectionSemaphore.Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,24 +42,11 @@ namespace ArchiSteamFarm {
|
|||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
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);
|
||||
|
||||
internal uint CellID {
|
||||
get => _CellID;
|
||||
set {
|
||||
if ((value == 0) || (_CellID == value)) {
|
||||
return;
|
||||
}
|
||||
|
||||
_CellID = value;
|
||||
Save();
|
||||
}
|
||||
}
|
||||
|
||||
[JsonProperty(Required = Required.DisallowNull)]
|
||||
private uint _CellID;
|
||||
[JsonProperty(PropertyName = "_CellID", Required = Required.DisallowNull)]
|
||||
internal uint CellID { get; private set; }
|
||||
|
||||
private string FilePath;
|
||||
|
||||
|
@ -70,13 +57,20 @@ namespace ArchiSteamFarm {
|
|||
}
|
||||
|
||||
FilePath = filePath;
|
||||
Save();
|
||||
Save().Wait();
|
||||
}
|
||||
|
||||
// This constructor is used only by deserializer
|
||||
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) {
|
||||
if (string.IsNullOrEmpty(filePath)) {
|
||||
|
@ -136,35 +130,46 @@ namespace ArchiSteamFarm {
|
|||
}
|
||||
}
|
||||
|
||||
Save();
|
||||
await Save().ConfigureAwait(false);
|
||||
} finally {
|
||||
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);
|
||||
if (string.IsNullOrEmpty(json)) {
|
||||
ASF.ArchiLogger.LogNullError(nameof(json));
|
||||
return;
|
||||
}
|
||||
|
||||
lock (FileLock) {
|
||||
string newFilePath = FilePath + ".new";
|
||||
string newFilePath = FilePath + ".new";
|
||||
|
||||
try {
|
||||
File.WriteAllText(newFilePath, json);
|
||||
await FileSemaphore.WaitAsync().ConfigureAwait(false);
|
||||
|
||||
if (File.Exists(FilePath)) {
|
||||
File.Replace(newFilePath, FilePath, null);
|
||||
} else {
|
||||
File.Move(newFilePath, FilePath);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
ASF.ArchiLogger.LogGenericException(e);
|
||||
try {
|
||||
File.WriteAllText(newFilePath, json);
|
||||
|
||||
if (File.Exists(FilePath)) {
|
||||
File.Replace(newFilePath, FilePath, null);
|
||||
} else {
|
||||
File.Move(newFilePath, FilePath);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
ASF.ArchiLogger.LogGenericException(e);
|
||||
} finally {
|
||||
FileSemaphore.Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,16 +38,8 @@ namespace ArchiSteamFarm {
|
|||
internal readonly ProtocolTypes ProtocolTypes;
|
||||
|
||||
internal ServerRecordEndPoint(string host, ushort port, ProtocolTypes protocolTypes) {
|
||||
if (string.IsNullOrEmpty(host)) {
|
||||
throw new ArgumentNullException(nameof(host));
|
||||
}
|
||||
|
||||
if (port == 0) {
|
||||
throw new ArgumentNullException(nameof(port));
|
||||
}
|
||||
|
||||
if (protocolTypes == 0) {
|
||||
throw new ArgumentNullException(nameof(protocolTypes));
|
||||
if (string.IsNullOrEmpty(host) || (port == 0) || (protocolTypes == 0)) {
|
||||
throw new ArgumentNullException(nameof(host) + " || " + nameof(port) + " || " + nameof(protocolTypes));
|
||||
}
|
||||
|
||||
Host = host;
|
||||
|
|
|
@ -40,7 +40,7 @@ namespace ArchiSteamFarm {
|
|||
private const string URL = "https://" + SharedInfo.StatisticsServer;
|
||||
|
||||
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 LastHeartBeat = DateTime.MinValue;
|
||||
|
@ -49,7 +49,7 @@ namespace ArchiSteamFarm {
|
|||
|
||||
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() {
|
||||
// Request persona update if needed
|
||||
|
@ -62,7 +62,7 @@ namespace ArchiSteamFarm {
|
|||
return;
|
||||
}
|
||||
|
||||
await Semaphore.WaitAsync().ConfigureAwait(false);
|
||||
await RequestsSemaphore.WaitAsync().ConfigureAwait(false);
|
||||
|
||||
try {
|
||||
if (!ShouldSendHeartBeats || (DateTime.UtcNow < LastHeartBeat.AddMinutes(MinHeartBeatTTL))) {
|
||||
|
@ -80,7 +80,7 @@ namespace ArchiSteamFarm {
|
|||
LastHeartBeat = DateTime.UtcNow;
|
||||
}
|
||||
} finally {
|
||||
Semaphore.Release();
|
||||
RequestsSemaphore.Release();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -114,7 +114,7 @@ namespace ArchiSteamFarm {
|
|||
}
|
||||
}
|
||||
|
||||
await Semaphore.WaitAsync().ConfigureAwait(false);
|
||||
await RequestsSemaphore.WaitAsync().ConfigureAwait(false);
|
||||
|
||||
try {
|
||||
if (DateTime.UtcNow < LastAnnouncementCheck.AddHours(MinAnnouncementCheckTTL)) {
|
||||
|
@ -154,7 +154,7 @@ namespace ArchiSteamFarm {
|
|||
ShouldSendHeartBeats = true;
|
||||
}
|
||||
} finally {
|
||||
Semaphore.Release();
|
||||
RequestsSemaphore.Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,7 +34,7 @@ using Newtonsoft.Json;
|
|||
using Newtonsoft.Json.Linq;
|
||||
|
||||
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
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
public void Dispose() => HttpClient.Dispose();
|
||||
|
||||
internal static void Init() {
|
||||
// Set max connection limit from default of 2 to desired value
|
||||
ServicePointManager.DefaultConnectionLimit = MaxConnections;
|
||||
|
|
Loading…
Reference in a new issue