diff --git a/ArchiSteamFarm/ArchiHandler.cs b/ArchiSteamFarm/ArchiHandler.cs index 9918b1795..41fd9f363 100644 --- a/ArchiSteamFarm/ArchiHandler.cs +++ b/ArchiSteamFarm/ArchiHandler.cs @@ -188,6 +188,21 @@ namespace ArchiSteamFarm { PlayGames(new HashSet { gameID }); } + internal void PlayGames(ConcurrentHashSet gameIDs) { + if ((gameIDs == null) || !Client.IsConnected) { + return; + } + + ClientMsgProtobuf request = new ClientMsgProtobuf(EMsg.ClientGamesPlayed); + foreach (uint gameID in gameIDs.Where(gameID => gameID != 0)) { + request.Body.games_played.Add(new CMsgClientGamesPlayed.GamePlayed { + game_id = new GameID(gameID) + }); + } + + Client.Send(request); + } + internal void PlayGames(HashSet gameIDs) { if ((gameIDs == null) || !Client.IsConnected) { return; diff --git a/ArchiSteamFarm/ArchiSteamFarm.csproj b/ArchiSteamFarm/ArchiSteamFarm.csproj index eb8982159..af5fcbf34 100644 --- a/ArchiSteamFarm/ArchiSteamFarm.csproj +++ b/ArchiSteamFarm/ArchiSteamFarm.csproj @@ -99,6 +99,8 @@ + + diff --git a/ArchiSteamFarm/CardsFarmer.cs b/ArchiSteamFarm/CardsFarmer.cs index 52995684f..299fc2f65 100755 --- a/ArchiSteamFarm/CardsFarmer.cs +++ b/ArchiSteamFarm/CardsFarmer.cs @@ -35,7 +35,7 @@ using System.Threading.Tasks; namespace ArchiSteamFarm { internal sealed class CardsFarmer { internal readonly ConcurrentDictionary GamesToFarm = new ConcurrentDictionary(); - internal readonly HashSet CurrentGamesFarming = new HashSet(); + internal readonly ConcurrentHashSet CurrentGamesFarming = new ConcurrentHashSet(); private readonly ManualResetEventSlim FarmResetEvent = new ManualResetEventSlim(false); private readonly SemaphoreSlim FarmingSemaphore = new SemaphoreSlim(1); @@ -142,8 +142,7 @@ namespace ArchiSteamFarm { } } while (await IsAnythingToFarm().ConfigureAwait(false)); - CurrentGamesFarming.Clear(); - CurrentGamesFarming.TrimExcess(); + CurrentGamesFarming.ClearAndTrim(); NowFarming = false; Logging.LogGenericInfo("Farming finished!", Bot.BotName); @@ -367,16 +366,14 @@ namespace ArchiSteamFarm { } if (maxHour >= 2) { - CurrentGamesFarming.Clear(); - CurrentGamesFarming.TrimExcess(); + CurrentGamesFarming.ClearAndTrim(); return true; } Logging.LogGenericInfo("Now farming: " + string.Join(", ", CurrentGamesFarming), Bot.BotName); bool result = FarmHours(maxHour, CurrentGamesFarming); - CurrentGamesFarming.Clear(); - CurrentGamesFarming.TrimExcess(); + CurrentGamesFarming.ClearAndTrim(); return result; } @@ -390,8 +387,7 @@ namespace ArchiSteamFarm { Logging.LogGenericInfo("Now farming: " + appID, Bot.BotName); bool result = await Farm(appID).ConfigureAwait(false); - CurrentGamesFarming.Clear(); - CurrentGamesFarming.TrimExcess(); + CurrentGamesFarming.ClearAndTrim(); if (!result) { return false; @@ -436,7 +432,7 @@ namespace ArchiSteamFarm { return success; } - private bool FarmHours(float maxHour, HashSet appIDs) { + private bool FarmHours(float maxHour, ConcurrentHashSet appIDs) { if ((maxHour < 0) || (appIDs == null) || (appIDs.Count == 0)) { return false; } diff --git a/ArchiSteamFarm/ConcurrentEnumerator.cs b/ArchiSteamFarm/ConcurrentEnumerator.cs new file mode 100644 index 000000000..6ec5614b3 --- /dev/null +++ b/ArchiSteamFarm/ConcurrentEnumerator.cs @@ -0,0 +1,59 @@ +/* + _ _ _ ____ _ _____ + / \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___ + / _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \ + / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | | +/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_| + + Copyright 2015-2016 Łukasz "JustArchi" Domeradzki + Contact: JustArchi@JustArchi.net + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Threading; + +namespace ArchiSteamFarm { + internal sealed class ConcurrentEnumerator : IEnumerator { + public T Current => Enumerator.Current; + + object IEnumerator.Current => Current; + + private readonly IEnumerator Enumerator; + private readonly ReaderWriterLockSlim Lock; + + internal ConcurrentEnumerator(ICollection collection, ReaderWriterLockSlim @lock) { + if ((collection == null) || (@lock == null)) { + throw new ArgumentNullException(nameof(collection) + " || " + nameof(@lock)); + } + + @lock.EnterReadLock(); + + Lock = @lock; + Enumerator = collection.GetEnumerator(); + } + + public bool MoveNext() => Enumerator.MoveNext(); + public void Reset() => Enumerator.Reset(); + + public void Dispose() { + if (Lock != null) { + Lock.ExitReadLock(); + } + } + } +} diff --git a/ArchiSteamFarm/ConcurrentHashSet.cs b/ArchiSteamFarm/ConcurrentHashSet.cs new file mode 100644 index 000000000..dfa320354 --- /dev/null +++ b/ArchiSteamFarm/ConcurrentHashSet.cs @@ -0,0 +1,124 @@ +/* + _ _ _ ____ _ _____ + / \ _ __ ___ | |__ (_)/ ___| | |_ ___ __ _ _ __ ___ | ___|__ _ _ __ _ __ ___ + / _ \ | '__|/ __|| '_ \ | |\___ \ | __|/ _ \ / _` || '_ ` _ \ | |_ / _` || '__|| '_ ` _ \ + / ___ \ | | | (__ | | | || | ___) || |_| __/| (_| || | | | | || _|| (_| || | | | | | | | +/_/ \_\|_| \___||_| |_||_||____/ \__|\___| \__,_||_| |_| |_||_| \__,_||_| |_| |_| |_| + + Copyright 2015-2016 Łukasz "JustArchi" Domeradzki + Contact: JustArchi@JustArchi.net + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +*/ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Threading; + +namespace ArchiSteamFarm { + internal sealed class ConcurrentHashSet : ICollection, IDisposable { + private readonly HashSet HashSet = new HashSet(); + private readonly ReaderWriterLockSlim Lock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion); + + public bool IsReadOnly => false; + + public IEnumerator GetEnumerator() => new ConcurrentEnumerator(HashSet, Lock); + + [SuppressMessage("ReSharper", "UnusedMethodReturnValue.Global")] + public bool Add(T item) { + Lock.EnterWriteLock(); + + try { + return HashSet.Add(item); + } finally { + Lock.ExitWriteLock(); + } + } + + public void Clear() { + Lock.EnterWriteLock(); + + try { + HashSet.Clear(); + } finally { + Lock.ExitWriteLock(); + } + } + + public void ClearAndTrim() { + Lock.EnterWriteLock(); + + try { + HashSet.Clear(); + HashSet.TrimExcess(); + } finally { + Lock.ExitWriteLock(); + } + } + + public bool Contains(T item) { + Lock.EnterReadLock(); + + try { + return HashSet.Contains(item); + } finally { + Lock.ExitReadLock(); + } + } + + public bool Remove(T item) { + Lock.EnterWriteLock(); + + try { + return HashSet.Remove(item); + } finally { + Lock.ExitWriteLock(); + } + } + + public int Count { + get { + Lock.EnterReadLock(); + + try { + return HashSet.Count; + } finally { + Lock.ExitReadLock(); + } + } + } + + public void Dispose() { + if (Lock != null) { + Lock.Dispose(); + } + } + + public void CopyTo(T[] array, int arrayIndex) { + Lock.EnterReadLock(); + + try { + HashSet.CopyTo(array, arrayIndex); + } finally { + Lock.ExitReadLock(); + } + } + + void ICollection.Add(T item) => Add(item); + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } +} \ No newline at end of file