LoggedInElsewhere improvements

Previously we had rather half-blind mechanism of trying to play given games, expecting a disconnect, and reconnecting in AccountPlayingDelay minutes. Now instead we have proper event-based handling - ASF listens on each PlayingSessionState event and updates availability of farming accordingly, not only resuming farming immediately after it's possible, but also limiting number of reconnects and other issues which could be caused by previous approach.

I bet this can be even further improved to not receive LoggedInElsewhere and disconnect error when user starts playing a game, but that's misc compared to gigantic improvement in this commit
This commit is contained in:
JustArchi
2016-05-30 00:09:42 +02:00
parent a0215d2ac4
commit 9403985b14
6 changed files with 61 additions and 28 deletions

View File

@@ -105,6 +105,19 @@ namespace ArchiSteamFarm {
}
}
internal sealed class PlayingSessionStateCallback : CallbackMsg {
internal readonly bool PlayingBlocked;
internal PlayingSessionStateCallback(JobID jobID, CMsgClientPlayingSessionState msg) {
if ((jobID == null) || (msg == null)) {
throw new ArgumentNullException(nameof(jobID) + " || " + nameof(msg));
}
JobID = jobID;
PlayingBlocked = msg.playing_blocked;
}
}
internal sealed class PurchaseResponseCallback : CallbackMsg {
internal enum EPurchaseResult : sbyte {
[SuppressMessage("ReSharper", "UnusedMember.Global")]
@@ -278,6 +291,9 @@ namespace ArchiSteamFarm {
case EMsg.ClientItemAnnouncements:
HandleItemAnnouncements(packetMsg);
break;
case EMsg.ClientPlayingSessionState:
HandlePlayingSessionState(packetMsg);
break;
case EMsg.ClientPurchaseResponse:
HandlePurchaseResponse(packetMsg);
break;
@@ -305,6 +321,15 @@ namespace ArchiSteamFarm {
Client.PostCallback(new NotificationsCallback(packetMsg.TargetJobID, response.Body));
}
private void HandlePlayingSessionState(IPacketMsg packetMsg) {
if (packetMsg == null) {
return;
}
ClientMsgProtobuf<CMsgClientPlayingSessionState> response = new ClientMsgProtobuf<CMsgClientPlayingSessionState>(packetMsg);
Client.PostCallback(new PlayingSessionStateCallback(packetMsg.TargetJobID, response.Body));
}
private void HandlePurchaseResponse(IPacketMsg packetMsg) {
if (packetMsg == null) {
return;

View File

@@ -67,8 +67,9 @@ namespace ArchiSteamFarm {
private readonly Trading Trading;
internal bool KeepRunning { get; private set; }
internal bool PlayingBlocked { get; private set; }
private bool InvalidPassword, LoggedInElsewhere, FirstTradeSent, SkipFirstShutdown;
private bool FirstTradeSent, InvalidPassword, SkipFirstShutdown;
private string AuthCode, TwoFactorCode;
internal static async Task RefreshCMs(uint cellID) {
@@ -192,6 +193,7 @@ namespace ArchiSteamFarm {
CallbackManager.Subscribe<ArchiHandler.NotificationsCallback>(OnNotifications);
CallbackManager.Subscribe<ArchiHandler.OfflineMessageCallback>(OnOfflineMessage);
CallbackManager.Subscribe<ArchiHandler.PlayingSessionStateCallback>(OnPlayingSessionState);
CallbackManager.Subscribe<ArchiHandler.PurchaseResponseCallback>(OnPurchaseResponse);
ArchiWebHandler = new ArchiWebHandler(this);
@@ -1475,16 +1477,6 @@ namespace ArchiSteamFarm {
Logging.LogGenericInfo("Will retry after 25 minutes...", BotName);
await Utilities.SleepAsync(25 * 60 * 1000).ConfigureAwait(false); // Captcha disappears after around 20 minutes, so we make it 25
}
} else if (LoggedInElsewhere) {
LoggedInElsewhere = false;
if (Program.GlobalConfig.AccountPlayingDelay == 0) {
Stop();
return;
}
Logging.LogGenericInfo("Account is being used elsewhere, ASF will try to resume farming in " + Program.GlobalConfig.AccountPlayingDelay + " minutes...", BotName);
await Utilities.SleepAsync(Program.GlobalConfig.AccountPlayingDelay * 60 * 1000).ConfigureAwait(false);
}
if (!KeepRunning || SteamClient.IsConnected) {
@@ -1619,14 +1611,6 @@ namespace ArchiSteamFarm {
}
Logging.LogGenericInfo("Logged off of Steam: " + callback.Result, BotName);
switch (callback.Result) {
case EResult.AlreadyLoggedInElsewhere:
case EResult.LoggedInElsewhere:
case EResult.LogonSessionReplaced:
LoggedInElsewhere = true;
break;
}
}
private async void OnLoggedOn(SteamUser.LoggedOnCallback callback) {
@@ -1660,6 +1644,8 @@ namespace ArchiSteamFarm {
case EResult.OK:
Logging.LogGenericInfo("Successfully logged on!", BotName);
PlayingBlocked = false; // If playing is really blocked, we'll be notified in a callback, old status doesn't matter
if (callback.CellID != 0) {
Program.GlobalDatabase.CellID = callback.CellID;
}
@@ -1711,6 +1697,7 @@ namespace ArchiSteamFarm {
Task.Run(() => Trading.CheckTrades()).Forget();
await Utilities.SleepAsync(1000).ConfigureAwait(false); // Wait a second for eventual PlayingSessionStateCallback
CardsFarmer.StartFarming().Forget();
break;
case EResult.NoConnection:
@@ -1810,6 +1797,26 @@ namespace ArchiSteamFarm {
SteamFriends.RequestOfflineMessages();
}
private void OnPlayingSessionState(ArchiHandler.PlayingSessionStateCallback callback) {
if (callback == null) {
Logging.LogNullError(nameof(callback));
return;
}
if (callback.PlayingBlocked == PlayingBlocked) {
return; // No status update, we're not interested
}
if (callback.PlayingBlocked) {
PlayingBlocked = true;
Logging.LogGenericInfo("Account is currently being used, ASF will resume farming when it's free...", BotName);
} else {
PlayingBlocked = false;
Logging.LogGenericInfo("Account is no longer occupied, farming process resumed!", BotName);
CardsFarmer.StartFarming().Forget();
}
}
private void OnPurchaseResponse(ArchiHandler.PurchaseResponseCallback callback) {
if (callback == null) {
return;

View File

@@ -80,13 +80,13 @@ namespace ArchiSteamFarm {
}
internal async Task StartFarming() {
if (NowFarming || ManualMode) {
if (NowFarming || ManualMode || Bot.PlayingBlocked) {
return;
}
await FarmingSemaphore.WaitAsync().ConfigureAwait(false);
if (NowFarming || ManualMode) {
if (NowFarming || ManualMode || Bot.PlayingBlocked) {
FarmingSemaphore.Release(); // We have nothing to do, don't forget to release semaphore
return;
}
@@ -99,6 +99,14 @@ namespace ArchiSteamFarm {
}
Logging.LogGenericInfo("We have a total of " + GamesToFarm.Count + " games to farm on this account...", Bot.BotName);
// This is the last moment for final check if we can farm
if (Bot.PlayingBlocked) {
Logging.LogGenericInfo("But account is currently occupied, so farming is stopped!");
FarmingSemaphore.Release(); // We have nothing to do, don't forget to release semaphore
return;
}
NowFarming = true;
FarmingSemaphore.Release(); // From this point we allow other calls to shut us down

View File

@@ -79,9 +79,6 @@ namespace ArchiSteamFarm {
[JsonProperty(Required = Required.DisallowNull)]
internal byte FarmingDelay { get; private set; } = DefaultFarmingDelay;
[JsonProperty(Required = Required.DisallowNull)]
internal byte AccountPlayingDelay { get; private set; } = 5;
[JsonProperty(Required = Required.DisallowNull)]
internal byte LoginLimiterDelay { get; private set; } = 7;

View File

@@ -9,7 +9,6 @@
"MaxFarmingTime": 10,
"IdleFarmingPeriod": 3,
"FarmingDelay": 5,
"AccountPlayingDelay": 5,
"LoginLimiterDelay": 7,
"InventoryLimiterDelay": 3,
"ForceHttp": false,

View File

@@ -78,9 +78,6 @@ namespace ConfigGenerator {
[JsonProperty(Required = Required.DisallowNull)]
public byte FarmingDelay { get; set; } = DefaultFarmingDelay;
[JsonProperty(Required = Required.DisallowNull)]
public byte AccountPlayingDelay { get; set; } = 5;
[JsonProperty(Required = Required.DisallowNull)]
public byte LoginLimiterDelay { get; set; } = 7;