Previous implementation was quite naive and assumed a lot of non-guaranteed premises. Checking if our session is valid before doing each request is very stupid, but it was needed for multi-user sessions. Next, even if we checked that our session is valid (or revalidated it if needed), we then assumed it's valid for at least X seconds, which is once again entirely false. Moreover, on top of those two issues, refreshing our session could do absolutely nothing as there is no guarantee that once-refreshes session stays like that even for our very next request.
Get rid of all of this shit and rewrite it with proper mechanism. We're making a request, if that request results in redirection to session refresh, refresh session, then repeat the same request again. Add failsafes for infinite loops, make it enough thread-safe and optimized for concurrent usage.
This commit won't only improve previously half-valid implementation, but will also greatly optimize number of requests being sent, as we won't need to check our session before each request. The entire thing should work like that since beginning.
Initially the issue was observed in #697, but that itself wasn't exactly what was fixed here, as multiple evaluation of the same trade is still wanted scenario.
The real issue was reported in http://steamcommunity.com/groups/ascfarm/discussions/1/2425614539578192287/
In a huge TL;DR, Steam is now sending trades notification each time something fetches current trade offers, be it ASF, the user, or some other script.
This will lead to possible ASF trade loop, as we'll get wanted notification about new trades, fetch them, leave some trades untouched, get new notification about trades and so on.
Initially I wanted to fix this in dirty way by just ignoring any extra notifications that happened since API call until 5 extra seconds after we were done with entire parsing, but I found much better solution - Steam actually includes extra info about amount of trades/items in notification (makes sense, since Steam client displays that info too). We can make use of that info and simply ignore any extra notification that results in same or smaller count.
Thanks to that we didn't only add a decent workaround for this recent Steam fuckup, but we also improved internal ASF code that will no longer schedule extra parsing if we accepted/rejected only some of the trades, making me happy with the actual solution.
As discussed in http://steamcommunity.com/groups/ascfarm/discussions/1/1483232961032339275/ - it's possible that SendOnFarmingFinished + ShutdownOnFarmingFinished bot will shutdown before master receives the trade and accepts it through default trading preferences.
This can be easily solved by caching SteamID and using it instead. While I'm at it, let's rewrite other parts that made use of SteamID as well.
Gifts/Login limiter is actually working decent, because response is nearly instant and fast enough to not worry about it in long-run.
With inventory things are entirely different, as inventory fetching might take even a very long time, and while fetching one inventory, we might already run out of our delay and start fetching another one.
This is not a big functionality-wise, as it's nothing new for ASF to parse multiple inventories concurrently, but Steam Community actually counts number of requests, and our inventory function might ask for multiple pages during execution, which could quickly lead to a situation of 10+ ongoing inventory requests being sent concurrently for too many accounts at once, as we can't predict not only how long the request will be handled, but also how many sub-requests we will do across one.
This means that for optimal performance in terms of rate-limiting, we must limit ASF to one inventory request at a time, with mandatory InventoryLimiterDelay before asking for another one.
This can degrade performance of previously fast !loot requests on multiple accounts at once (especially with bigger inventories), but it will also decrease significantly a chance of getting rate-limited and requests failing.
DateTime.UtcNow is from at least 30 up to even 117 times faster than DateTime.Now. We have many places in which we compare our date to previous one we set ourselves - in all those places we can (and should) use UtcNow instead of Now.
Local DateTime is still around in some places, those that are user-specific, such as when logging messages.
Initially I wanted to use this to build reliable statistics as in, number of active/inactive accounts, total accounts and so on
However, I don't really think my server will be able to handle it with more and more users, even if reporting that was active so far was still bare minimum.
Let's just stick to STM-only reporting for now, I don't have enough money for anything more advanced for now.