Skip to content

Commit c2d017e

Browse files
authored
Merge pull request #111 from maxisoft/dev
Fix various missing method or class loading due to ASF update
2 parents 847e9de + 2d49d75 commit c2d017e

File tree

11 files changed

+160
-30
lines changed

11 files changed

+160
-30
lines changed

.editorconfig

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ indent_style = tab
1010
insert_final_newline = true
1111
trim_trailing_whitespace = true
1212

13+
[*.{cs,vb}]
14+
dotnet_diagnostic.CA1859.severity = none
15+
1316
###############################
1417
# C# Coding Conventions #
1518
###############################

.github/workflows/publish.yml

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,11 @@ jobs:
1616
strategy:
1717
fail-fast: false
1818
matrix:
19-
os: [macos-latest, ubuntu-latest, windows-latest]
19+
os: [
20+
macos-latest,
21+
ubuntu-latest,
22+
#windows-latest
23+
]
2024

2125
runs-on: ${{ matrix.os }}
2226

@@ -190,13 +194,10 @@ jobs:
190194
- name: Checkout code
191195
uses: actions/checkout@v4.2.2
192196

193-
# TODO: It'd be perfect if we could match final artifacts to the platform they target, so e.g. linux build comes from the linux machine
194-
# However, that is currently impossible due to https://github.com/dotnet/msbuild/issues/3897
195-
# Therefore, we'll (sadly) pull artifacts from Windows machine only for now
196-
- name: Download generic artifact from windows-latest
197+
- name: Download generic artifact from ubuntu-latest
197198
uses: actions/download-artifact@v4.1.8
198199
with:
199-
name: windows-latest_${{ env.PLUGIN_NAME }}-generic
200+
name: ubuntu-latest_${{ env.PLUGIN_NAME }}-generic
200201
path: out
201202

202203
- name: Unzip and copy generic artifact

ASFFreeGames/ASFFreeGamesPlugin.cs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ internal static PluginContext Context {
4040
}
4141

4242
// ReSharper disable once InconsistentNaming
43-
private static readonly AsyncLocal<PluginContext> _context = new();
43+
private static readonly Utils.Workarounds.AsyncLocal<PluginContext> _context = new();
4444
private static CancellationToken CancellationToken => Context.CancellationToken;
4545

4646
public string Name => StaticName;
@@ -141,7 +141,9 @@ public async void CollectGamesOnClock(object? source) {
141141

142142
if (!cts.IsCancellationRequested) {
143143
string cmd = $"FREEGAMES {FreeGamesCommand.CollectInternalCommandString} " + string.Join(' ', reorderedBots.Select(static bot => bot.BotName));
144+
#pragma warning disable CS1998
144145
await OnBotCommand(null!, EAccess.None, cmd, cmd.Split()).ConfigureAwait(false);
146+
#pragma warning restore CS1998
145147
}
146148
}
147149
}
@@ -212,12 +214,15 @@ private async Task RemoveBot(Bot bot) {
212214
private void StartTimerIfNeeded() => CollectIntervalManager.StartTimerIfNeeded();
213215

214216
~ASFFreeGamesPlugin() => CollectIntervalManager.Dispose();
215-
public readonly GithubPluginUpdater Updater = new(new Lazy<Version>(GetVersion));
217+
218+
#region IGitHubPluginUpdates implementation
219+
private readonly GithubPluginUpdater Updater = new(new Lazy<Version>(GetVersion));
216220
string IGitHubPluginUpdates.RepositoryName => GithubPluginUpdater.RepositoryName;
217221

218222
bool IGitHubPluginUpdates.CanUpdate => Updater.CanUpdate;
219223

220224
Task<Uri?> IGitHubPluginUpdates.GetTargetReleaseURL(Version asfVersion, string asfVariant, bool asfUpdate, bool stable, bool forced) => Updater.GetTargetReleaseURL(asfVersion, asfVariant, asfUpdate, stable, forced);
225+
#endregion
221226
}
222227

223228
#pragma warning restore CA1812 // ASF uses this class during runtime

ASFFreeGames/Commands/FreeGamesCommand.cs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -156,9 +156,18 @@ public void Dispose() {
156156
private async ValueTask<string?> HandleInternalCollectCommand(Bot? bot, string[] args, CancellationToken cancellationToken) {
157157
Dictionary<string, Bot> botMap = Context.Bots.ToDictionary(static b => b.BotName.Trim(), static b => b, StringComparer.InvariantCultureIgnoreCase);
158158

159-
Bot[] bots = args.Skip(2).Select(botName => botMap.GetValueOrDefault(botName.Trim())).Where(static b => b is not null).ToArray()!;
159+
List<Bot> bots = [];
160160

161-
if (bots.Length == 0) {
161+
for (int i = 2; i < args.Length; i++) {
162+
string botName = args[i].Trim();
163+
164+
// ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
165+
if (botMap.TryGetValue(botName, out Bot? savedBot) && savedBot is not null) {
166+
bots.Add(savedBot);
167+
}
168+
}
169+
170+
if (bots.Count == 0) {
162171
if (bot is null) {
163172
return null;
164173
}
@@ -168,7 +177,7 @@ public void Dispose() {
168177

169178
int collected = await CollectGames(bots, ECollectGameRequestSource.Scheduled, cancellationToken).ConfigureAwait(false);
170179

171-
return FormatBotResponse(bot, $"Collected a total of {collected} free game(s)" + (bots.Length > 1 ? $" on {bots.Length} bots" : $" on {bots.FirstOrDefault()?.BotName}"));
180+
return FormatBotResponse(bot, $"Collected a total of {collected} free game(s)" + (bots.Count > 1 ? $" on {bots.Count} bots" : $" on {bots.FirstOrDefault()?.BotName}"));
172181
}
173182

174183
private async Task SaveOptions(CancellationToken cancellationToken) {

ASFFreeGames/Configurations/ASFFreeGamesOptionsLoader.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -59,13 +59,12 @@ public static async Task Save(ASFFreeGamesOptions options, CancellationToken can
5959
try {
6060
#pragma warning disable CAC001
6161
#pragma warning disable CA2007
62-
63-
// Use FileOptions.Asynchronous when creating a file stream for async operations
64-
await using FileStream fs = new(path, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite, 4096, FileOptions.Asynchronous);
62+
await using FileStream fs = new(path, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.ReadWrite);
6563
#pragma warning restore CA2007
6664
#pragma warning restore CAC001
67-
using IMemoryOwner<byte> buffer = MemoryPool<byte>.Shared.Rent(checked(fs.Length > 0 ? (int) fs.Length + 1 : 1 << 15));
68-
int read = await fs.ReadAsync(buffer.Memory, cancellationToken).ConfigureAwait(false);
65+
byte[] buffer = new byte[fs.Length > 0 ? (int) fs.Length + 1 : 1 << 15];
66+
67+
int read = await fs.ReadAsync(buffer, cancellationToken).ConfigureAwait(false);
6968

7069
try {
7170
fs.Position = 0;
@@ -76,7 +75,8 @@ public static async Task Save(ASFFreeGamesOptions options, CancellationToken can
7675

7776
catch (Exception) {
7877
fs.Position = 0;
79-
await fs.WriteAsync(buffer.Memory[..read], cancellationToken).ConfigureAwait(false);
78+
79+
await fs.WriteAsync(((ReadOnlyMemory<byte>) buffer)[..read], cancellationToken).ConfigureAwait(false);
8080
fs.SetLength(read);
8181

8282
throw;

ASFFreeGames/FreeGames/Strategies/RedlibListFreeGamesStrategy.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,14 @@ private async Task<IReadOnlyCollection<RedditGameEntry>> DoDownloadUsingInstance
155155

156156
long dateMillis = date.ToUnixTimeMilliseconds();
157157

158-
return entries.Select(entry => entry.ToRedditGameEntry(dateMillis)).ToArray();
158+
List<RedditGameEntry> redditGameEntries = [];
159+
160+
// ReSharper disable once LoopCanBeConvertedToQuery
161+
foreach (RedlibGameEntry entry in entries) {
162+
redditGameEntries.Add(entry.ToRedditGameEntry(dateMillis));
163+
}
164+
165+
return redditGameEntries;
159166
}
160167

161168
private async Task<IReadOnlyCollection<RedditGameEntry>> DownloadUsingInstance(SimpleHttpClient client, Uri uri, uint retry, CancellationToken cancellationToken) {

ASFFreeGames/Github/GithubPluginUpdater.cs

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,32 +14,55 @@ public class GithubPluginUpdater(Lazy<Version> version) {
1414

1515
private Version CurrentVersion => version.Value;
1616

17+
private static void LogGenericError(string message) {
18+
if (string.IsNullOrEmpty(message)) {
19+
return;
20+
}
21+
22+
ArchiSteamFarm.Core.ASF.ArchiLogger.LogGenericError($"{nameof(GithubPluginUpdater)}: {message}");
23+
}
24+
25+
private static void LogGenericDebug(string message) {
26+
if (string.IsNullOrEmpty(message)) {
27+
return;
28+
}
29+
30+
ArchiSteamFarm.Core.ASF.ArchiLogger.LogGenericDebug($"{nameof(GithubPluginUpdater)}: {message}");
31+
}
32+
1733
public async Task<Uri?> GetTargetReleaseURL(Version asfVersion, string asfVariant, bool asfUpdate, bool stable, bool forced) {
1834
ArgumentNullException.ThrowIfNull(asfVersion);
1935
ArgumentException.ThrowIfNullOrEmpty(asfVariant);
2036

2137
if (!CanUpdate) {
38+
LogGenericDebug("CanUpdate is false");
39+
2240
return null;
2341
}
2442

2543
if (string.IsNullOrEmpty(RepositoryName)) {
26-
//ArchiSteamFarm.Core.ASF.ArchiLogger.LogGenericError(Strings.FormatWarningFailedWithError(nameof(RepositoryName)));
44+
LogGenericError("RepositoryName is null or empty");
2745

2846
return null;
2947
}
3048

3149
ReleaseResponse? releaseResponse = await GitHubService.GetLatestRelease(RepositoryName).ConfigureAwait(false);
3250

3351
if (releaseResponse == null) {
52+
LogGenericError("GetLatestRelease returned null");
53+
3454
return null;
3555
}
3656

3757
if (releaseResponse.IsPreRelease) {
58+
LogGenericError("GetLatestRelease returned pre-release");
59+
3860
return null;
3961
}
4062

41-
if (stable && !((releaseResponse.PublishedAt - DateTime.UtcNow).Duration() > TimeSpan.FromHours(3))) {
42-
// Skip updates that are too recent
63+
if (stable && ((releaseResponse.PublishedAt - DateTime.UtcNow).Duration() < TimeSpan.FromHours(3))) {
64+
LogGenericDebug("GetLatestRelease returned too recent");
65+
4366
return null;
4467
}
4568

@@ -48,27 +71,25 @@ public class GithubPluginUpdater(Lazy<Version> version) {
4871
if (!forced && (CurrentVersion >= newVersion)) {
4972
// Allow same version to be re-updated when we're updating ASF release and more than one asset is found - potential compatibility difference
5073
if ((CurrentVersion > newVersion) || !asfUpdate || (releaseResponse.Assets.Count(static asset => asset.Name.EndsWith(".zip", StringComparison.OrdinalIgnoreCase)) < 2)) {
51-
//ASF.ArchiLogger.LogGenericInfo(Strings.FormatPluginUpdateNotFound(Name, Version, newVersion));
52-
5374
return null;
5475
}
5576
}
5677

5778
if (releaseResponse.Assets.Count == 0) {
58-
//ASF.ArchiLogger.LogGenericWarning(Strings.FormatPluginUpdateNoAssetFound(Name, Version, newVersion));
79+
LogGenericError($"GetLatestRelease for version {newVersion} returned no assets");
5980

6081
return null;
6182
}
6283

6384
ReleaseAsset? asset = releaseResponse.Assets.FirstOrDefault(static asset => asset.Name.EndsWith(".zip", StringComparison.OrdinalIgnoreCase) && (asset.Size > (1 << 18)));
6485

6586
if ((asset == null) || !releaseResponse.Assets.Contains(asset)) {
66-
//ASF.ArchiLogger.LogGenericWarning(Strings.FormatPluginUpdateNoAssetFound(Name, Version, newVersion));
87+
LogGenericError($"GetLatestRelease for version {newVersion} returned no valid assets");
6788

6889
return null;
6990
}
7091

71-
//.ArchiLogger.LogGenericInfo(Strings.FormatPluginUpdateFound(Name, Version, newVersion));
92+
LogGenericDebug($"GetLatestRelease for version {newVersion} returned asset {asset.Name} with url {asset.DownloadURL}");
7293

7394
return asset.DownloadURL;
7495
}

ASFFreeGames/Reddit/RedditHelper.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ IReadOnlyCollection<RedditGameEntry> returnValue() {
4343
}
4444

4545
// ReSharper disable once LoopCanBePartlyConvertedToQuery
46-
foreach (JsonNode? comment in children.AsArray()) {
46+
foreach (JsonNode? comment in (JsonArray) children) {
4747
JsonNode? commentData = comment?["data"];
4848

4949
if (commentData is null) {

ASFFreeGames/Redlib/Instances/RedlibInstanceList.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,10 +89,10 @@ internal static List<Uri> ParseUrls(JsonNode json) {
8989
return [];
9090
}
9191

92-
List<Uri> uris = new(instances.AsArray().Count);
92+
List<Uri> uris = new(((JsonArray) instances).Count);
9393

9494
// ReSharper disable once LoopCanBePartlyConvertedToQuery
95-
foreach (JsonNode? instance in instances.AsArray()) {
95+
foreach (JsonNode? instance in (JsonArray) instances) {
9696
JsonNode? url = instance?["url"];
9797

9898
if (Uri.TryCreate(url?.GetValue<string>() ?? "", UriKind.Absolute, out Uri? instanceUri) && instanceUri.Scheme is "http" or "https") {
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
using System;
2+
using System.Reflection;
3+
4+
namespace Maxisoft.ASF.Utils.Workarounds;
5+
6+
public sealed class AsyncLocal<T> {
7+
// ReSharper disable once StaticMemberInGenericType
8+
private static readonly Type? AsyncLocalType;
9+
10+
#pragma warning disable CA1810
11+
static AsyncLocal() {
12+
#pragma warning restore CA1810
13+
try {
14+
AsyncLocalType = Type.GetType("System.Threading.AsyncLocal`1")
15+
?.MakeGenericType(typeof(T));
16+
}
17+
catch (InvalidOperationException) {
18+
// ignore
19+
}
20+
21+
try {
22+
AsyncLocalType ??= Type.GetType("System.Threading.AsyncLocal")
23+
?.MakeGenericType(typeof(T));
24+
}
25+
26+
catch (InvalidOperationException) {
27+
// ignore
28+
}
29+
}
30+
31+
private readonly object? Delegate;
32+
private T? NonSafeValue;
33+
34+
/// <summary>Instantiates an <see cref="AsyncLocal{T}"/> instance that does not receive change notifications.</summary>
35+
public AsyncLocal() {
36+
if (AsyncLocalType is not null) {
37+
try {
38+
Delegate = Activator.CreateInstance(AsyncLocalType)!;
39+
}
40+
catch (Exception) {
41+
// ignored
42+
}
43+
}
44+
}
45+
46+
/// <summary>Gets or sets the value of the ambient data.</summary>
47+
/// <value>The value of the ambient data. If no value has been set, the returned value is default(T).</value>
48+
public T? Value {
49+
get {
50+
if (Delegate is not null) {
51+
try {
52+
PropertyInfo? property = Delegate.GetType().GetProperty("Value");
53+
54+
if (property is not null) {
55+
return (T) property.GetValue(Delegate)!;
56+
}
57+
}
58+
catch (Exception) {
59+
// ignored
60+
}
61+
}
62+
63+
return (T) NonSafeValue!;
64+
}
65+
set {
66+
if (Delegate is not null) {
67+
try {
68+
PropertyInfo? property = Delegate.GetType().GetProperty("Value");
69+
70+
if (property is not null) {
71+
property.SetValue(Delegate, value);
72+
73+
return;
74+
}
75+
}
76+
catch (Exception) {
77+
// ignored
78+
}
79+
}
80+
81+
NonSafeValue = value;
82+
}
83+
}
84+
}

Directory.Build.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
<PropertyGroup>
55
<PluginName>ASFFreeGames</PluginName>
6-
<Version>1.8.0.0</Version>
6+
<Version>1.8.1.0</Version>
77
<TargetFramework>net8.0</TargetFramework>
88
</PropertyGroup>
99

0 commit comments

Comments
 (0)