Skip to content

Commit

Permalink
Merge pull request #20 from Citrinate/2.10
Browse files Browse the repository at this point in the history
Version 2.10
  • Loading branch information
Citrinate authored Jun 2, 2024
2 parents 04a280a + edac838 commit 1218ff5
Show file tree
Hide file tree
Showing 24 changed files with 1,574 additions and 502 deletions.
11 changes: 3 additions & 8 deletions BoosterManager/BoosterManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ namespace BoosterManager {
public sealed class BoosterManager : IASF, IBotModules, IBotCommand2, IBotTradeOfferResults, IGitHubPluginUpdates {
public string Name => nameof(BoosterManager);
public string RepositoryName => "Citrinate/BoosterManager";
public bool CanUpdate => !BoosterHandler.IsCraftingOneTimeBoosters();
public Version Version => typeof(BoosterManager).Assembly.GetName().Version ?? new Version("0");

public Task OnLoaded() {
Expand Down Expand Up @@ -45,11 +44,6 @@ public Task OnASFInit(IReadOnlyDictionary<string, JsonElement>? additionalConfig
BoosterHandler.AllowCraftUnmarketableBoosters = configProperty.Value.GetBoolean();
break;
}
case "BoosterDelayBetweenBots" when configProperty.Value.ValueKind == JsonValueKind.Number: {
ASF.ArchiLogger.LogGenericInfo("Booster Delay Between Bots : " + configProperty.Value);
BoosterHandler.UpdateBotDelays(configProperty.Value.GetInt32());
break;
}
case "BoosterDataAPI" when configProperty.Value.ValueKind == JsonValueKind.String: {
ASF.ArchiLogger.LogGenericInfo("Booster Data API : " + configProperty.Value);
DataHandler.BoosterDataAPI = new Uri(configProperty.Value.GetString()!);
Expand Down Expand Up @@ -92,7 +86,8 @@ public Task OnASFInit(IReadOnlyDictionary<string, JsonElement>? additionalConfig
}

public Task OnBotInitModules(Bot bot, IReadOnlyDictionary<string, JsonElement>? additionalConfigProperties = null) {
BoosterHandler.AddHandler(bot);
string databaseFilePath = Bot.GetFilePath(String.Format("{0}_{1}", bot.BotName, nameof(BoosterManager)), Bot.EFileType.Database);
BoosterHandler.AddHandler(bot, BoosterDatabase.CreateOrLoad(databaseFilePath));

if (additionalConfigProperties == null) {
return Task.FromResult(0);
Expand All @@ -106,7 +101,7 @@ public Task OnBotInitModules(Bot bot, IReadOnlyDictionary<string, JsonElement>?
if (gameIDs == null) {
bot.ArchiLogger.LogNullError(gameIDs);
} else {
BoosterHandler.BoosterHandlers[bot.BotName].SchedulePermanentBoosters(gameIDs);
BoosterHandler.BoosterHandlers[bot.BotName].ScheduleBoosters(BoosterJobType.Permanent, gameIDs.ToList(), StatusReporter.StatusLogger());
}
break;
}
Expand Down
2 changes: 1 addition & 1 deletion BoosterManager/BoosterManager.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

<PropertyGroup>
<Authors>Citrinate</Authors>
<AssemblyVersion>2.9.0.2</AssemblyVersion>
<AssemblyVersion>2.10.0.0</AssemblyVersion>
<Nullable>enable</Nullable>
<LangVersion>latest</LangVersion>
<TargetFramework>net8.0</TargetFramework>
Expand Down
46 changes: 26 additions & 20 deletions BoosterManager/Boosters/Booster.cs
Original file line number Diff line number Diff line change
@@ -1,30 +1,32 @@
using ArchiSteamFarm.Steam;
using SteamKit2;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace BoosterManager {
internal sealed class Booster {
private readonly Bot Bot;
private readonly BoosterQueue BoosterQueue;
private BoosterDatabase BoosterDatabase => BoosterHandler.BoosterHandlers[Bot.BotName].BoosterDatabase;
internal readonly BoosterJob BoosterJob;
internal readonly uint GameID;
internal readonly BoosterType Type;
internal readonly Steam.BoosterInfo Info;
private readonly DateTime InitTime;
private readonly BoosterLastCraft? LastCraft;
internal bool WasCrafted = false;
internal bool WasCrafted { get; private set; } = false;

internal Booster(Bot bot, uint gameID, BoosterType type, Steam.BoosterInfo info, BoosterQueue boosterQueue, BoosterLastCraft? lastCraft) {
internal Booster(Bot bot, uint gameID, Steam.BoosterInfo info, BoosterJob boosterJob) {
Bot = bot;
GameID = gameID;
Type = type;
Info = info;
InitTime = DateTime.Now;
BoosterQueue = boosterQueue;
LastCraft = lastCraft;
BoosterJob = boosterJob;
LastCraft = BoosterDatabase.GetLastCraft(gameID);
}

internal async Task<Steam.BoostersResponse?> Craft(TradabilityPreference nTp) {
internal async Task<Steam.BoostersResponse?> Craft(Steam.TradabilityPreference nTp) {
await BoosterDatabase.PreCraft(this).ConfigureAwait(false);

Steam.BoostersResponse? result = await WebRequest.CreateBooster(Bot, Info.AppID, Info.Series, nTp).ConfigureAwait(false);

if (result?.Result?.Result == EResult.OK) {
Expand All @@ -35,33 +37,37 @@ internal Booster(Bot bot, uint gameID, BoosterType type, Steam.BoosterInfo info,
}

internal void SetWasCrafted() {
BoosterQueue.UpdateLastCraft(GameID, DateTime.Now);
BoosterDatabase.PostCraft();
BoosterDatabase.SetLastCraft(GameID, DateTime.Now);
WasCrafted = true;
}

internal DateTime GetAvailableAtTime(int delayInSeconds = 0) {
internal DateTime GetAvailableAtTime() {
if (Info.Unavailable && Info.AvailableAtTime != null) {
if (LastCraft != null) {
// If this booster had a delay the last time it was crafted then, because of the 24 hour
// cooldown, that delay still exists, and doesn't need to be added in again. If the new delay
// is bigger then the old one, then we'll still need to delay some more.
delayInSeconds = Math.Max(0, delayInSeconds - LastCraft.BoosterDelay);
}

if (LastCraft == null
|| LastCraft.CraftTime.AddDays(1) > Info.AvailableAtTime.Value.AddMinutes(1)
|| (Info.AvailableAtTime.Value.AddMinutes(1) - LastCraft.CraftTime.AddDays(1)).TotalMinutes > 2 // LastCraft time is too old to be used
) {
// Unavailable boosters become available exactly 24 hours after being crafted, down to the second, but Steam
// doesn't tell us which second that is. To get around this, we try to save the exact craft time. If that
// fails, then we use Steam's time and round up a minute get a time we know the booster will be available at.
return Info.AvailableAtTime.Value.AddMinutes(1).AddSeconds(delayInSeconds);
return Info.AvailableAtTime.Value.AddMinutes(1);
}

return LastCraft.CraftTime.AddDays(1).AddSeconds(delayInSeconds);
return LastCraft.CraftTime.AddDays(1);
}

return InitTime.AddSeconds(delayInSeconds);
return InitTime;
}
}

internal class BoosterComparer : IEqualityComparer<Booster> {
public bool Equals(Booster? x, Booster? y) {
return x?.GameID == y?.GameID;
}

public int GetHashCode(Booster obj) {
return HashCode.Combine(obj.GameID);
}
}
}
45 changes: 37 additions & 8 deletions BoosterManager/Boosters/BoosterDatabase.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
Expand All @@ -12,6 +13,15 @@ internal sealed class BoosterDatabase : SerializableFile {
[JsonInclude]
private ConcurrentDictionary<uint, BoosterLastCraft> BoosterLastCrafts { get; init; } = new();

[JsonInclude]
internal List<BoosterJobState> BoosterJobs { get; private set; } = new();

[JsonInclude]
internal uint? CraftingGameID { get; private set; } = null;

[JsonInclude]
internal DateTime? CraftingTime { get; private set; } = null;

[JsonConstructor]
private BoosterDatabase() { }

Expand All @@ -25,7 +35,7 @@ private BoosterDatabase(string filePath) : this() {

protected override Task Save() => Save(this);

internal static BoosterDatabase? CreateOrLoad(string filePath) {
internal static BoosterDatabase CreateOrLoad(string filePath) {
if (string.IsNullOrEmpty(filePath)) {
throw new ArgumentNullException(nameof(filePath));
}
Expand All @@ -42,20 +52,20 @@ private BoosterDatabase(string filePath) : this() {
if (string.IsNullOrEmpty(json)) {
ASF.ArchiLogger.LogGenericError(string.Format(ArchiSteamFarm.Localization.Strings.ErrorIsEmpty, nameof(json)));

return null;
return new BoosterDatabase(filePath);
}

boosterDatabase = json.ToJsonObject<BoosterDatabase>();
} catch (Exception e) {
ASF.ArchiLogger.LogGenericException(e);

return null;
return new BoosterDatabase(filePath);
}

if (boosterDatabase == null) {
ASF.ArchiLogger.LogNullError(boosterDatabase);

return null;
return new BoosterDatabase(filePath);
}

boosterDatabase.FilePath = filePath;
Expand All @@ -79,15 +89,34 @@ private BoosterDatabase(string filePath) : this() {
return null;
}

internal void SetLastCraft(uint appID, DateTime craftTime, int boosterDelay) {
BoosterLastCraft newCraft = new BoosterLastCraft(craftTime, boosterDelay);
internal void SetLastCraft(uint appID, DateTime craftTime) {
BoosterLastCraft newCraft = new BoosterLastCraft(craftTime);
BoosterLastCrafts.AddOrUpdate(appID, newCraft, (key, oldCraft) => {
oldCraft.CraftTime = craftTime;
// boosterDelay might change, but the old delay will still be there, the real delay will be the bigger of the two
oldCraft.BoosterDelay = Math.Max(oldCraft.BoosterDelay, boosterDelay);

return oldCraft;
});

Utilities.InBackground(Save);
}

internal void UpdateBoosterJobs(List<BoosterJobState> boosterJobs) {
BoosterJobs = boosterJobs;

Utilities.InBackground(Save);
}

internal async Task PreCraft(Booster booster) {
CraftingGameID = booster.GameID;
CraftingTime = booster.Info.AvailableAtTime;

await Save().ConfigureAwait(false);
}

internal void PostCraft() {
CraftingGameID = null;
CraftingTime = null;

Utilities.InBackground(Save);
}
}
Expand Down
11 changes: 11 additions & 0 deletions BoosterManager/Boosters/BoosterDequeueReason.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
namespace BoosterManager {
internal enum BoosterDequeueReason {
AlreadyQueued,
Crafted,
RemovedByUser,
Uncraftable,
UnexpectedlyUncraftable,
Unmarketable,
JobStopped
}
}
Loading

0 comments on commit 1218ff5

Please sign in to comment.