Skip to content

Commit

Permalink
7.24.24
Browse files Browse the repository at this point in the history
- Updated PKHeX, and Discord.NET
- Better Socket Handling, will retry to connect to your switch several times if internet fails.
- Better handling for getting embed colors
- Better http request handling
- Handle Discord Disconnect/Reconnect
- Add Species/Form/Shiny to newly generated Mystery Raids so that data is already populated before the seed is injected.
- Specify a specific raid battler based on Mystery Raid Tera Type.  New settings in MysteryRaidSettings > Tera Type Battlers
  • Loading branch information
bdawg1989 committed Jul 25, 2024
1 parent cf068a5 commit 94dadcb
Show file tree
Hide file tree
Showing 14 changed files with 412 additions and 99 deletions.
113 changes: 96 additions & 17 deletions SysBot.Base/Connection/Switch/Wireless/SwitchSocketAsync.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Buffers;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Sockets;
using System.Text;
Expand Down Expand Up @@ -36,16 +37,35 @@ public override void Connect()
}

Log("Connecting to device...");
IAsyncResult result = Connection.BeginConnect(Info.IP, Info.Port, null, null);
bool success = result.AsyncWaitHandle.WaitOne(5000, true);
if (!success || !Connection.Connected)
int retryCount = 0;
const int maxRetries = 10;

while (retryCount < maxRetries)
{
InitializeSocket();
throw new Exception("Failed to connect to device.");
try
{
IAsyncResult result = Connection.BeginConnect(Info.IP, Info.Port, null, null);
bool success = result.AsyncWaitHandle.WaitOne(5000, true);
if (!success || !Connection.Connected)
{
throw new Exception("Failed to connect to device.");
}
Connection.EndConnect(result);
Log("Connected!");
Label = Name;
return;
}
catch (Exception ex)
{
retryCount++;
Log($"Connection attempt {retryCount} failed: {ex.Message}");
if (retryCount >= maxRetries)
{
throw;
}
Task.Delay(1000 * retryCount).Wait(); // Wait before retrying
}
}
Connection.EndConnect(result);
Log("Connected!");
Label = Name;
}

public override void Reset()
Expand Down Expand Up @@ -75,19 +95,42 @@ public override void Disconnect()
/// <summary> Only call this if you are sending small commands. </summary>
public async Task<int> SendAsync(byte[] buffer, CancellationToken token)
{
return await Connection.SendAsync(buffer, token).AsTask();
return await RetryOperation(async (ct) => await Connection.SendAsync(buffer, ct).AsTask(), token);
}

public async Task EnsureConnectedAsync(CancellationToken token)
{
if (!Connected)
{
Log("Connection lost. Attempting to reconnect...");
await RetryOperation(async (ct) =>
{
Reset();
Connect();
return true;
}, token);
}
}

private async Task<byte[]> ReadBytesFromCmdAsync(byte[] cmd, int length, CancellationToken token)
{
await SendAsync(cmd, token).ConfigureAwait(false);
var size = (length * 2) + 1;
var buffer = ArrayPool<byte>.Shared.Rent(size);
var mem = buffer.AsMemory()[..size];
await Connection.ReceiveAsync(mem, token);
var result = DecodeResult(mem, length);
ArrayPool<byte>.Shared.Return(buffer, true);
return result;
return await RetryOperation(async (ct) =>
{
await EnsureConnectedAsync(ct);
await SendAsync(cmd, ct).ConfigureAwait(false);
var size = (length * 2) + 1;
var buffer = ArrayPool<byte>.Shared.Rent(size);
try
{
var mem = buffer.AsMemory()[..size];
await Connection.ReceiveAsync(mem, ct);
return DecodeResult(mem, length);
}
finally
{
ArrayPool<byte>.Shared.Return(buffer, true);
}
}, token);
}

private static byte[] DecodeResult(ReadOnlyMemory<byte> buffer, int length)
Expand Down Expand Up @@ -157,6 +200,7 @@ public async Task<bool> IsProgramRunning(ulong pid, CancellationToken token)

private async Task<byte[]> Read(ulong offset, int length, SwitchOffsetType type, CancellationToken token)
{
await EnsureConnectedAsync(token);
var method = type.GetReadMethod();
if (length <= MaximumTransferSize)
{
Expand Down Expand Up @@ -296,5 +340,40 @@ public async Task<long> GetUnixTime(CancellationToken token)
Array.Reverse(result);
return BitConverter.ToInt64(result, 0);
}

private void HandleDisconnect()
{
Log("Unexpected disconnection detected. Attempting to reconnect...");
try
{
Reset();
Connect();
}
catch (Exception ex)
{
LogError($"Failed to reconnect: {ex.Message}");
}
}

private async Task<T> RetryOperation<T>(Func<CancellationToken, Task<T>> operation, CancellationToken token, int maxRetries = 3)
{
int retryCount = 0;
while (true)
{
try
{
return await operation(token);
}
catch (Exception ex) when (ex is SocketException or IOException)
{
if (++retryCount > maxRetries)
throw;

int delay = (int)Math.Pow(2, retryCount) * 1000; // Exponential backoff
Log($"Connection error. Retrying in {delay}ms. Attempt {retryCount} of {maxRetries}");
await Task.Delay(delay, token);
}
}
}
}
}
2 changes: 1 addition & 1 deletion SysBot.Base/SysBot.Base.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<Configurations>Debug;Release;sysbottest</Configurations>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Discord.Net" Version="3.13.0" />
<PackageReference Include="Discord.Net" Version="3.15.3" />
<PackageReference Include="NLog" Version="5.2.7" />
<PackageReference Include="LibUsbDotNet" Version="2.2.29" />
<PackageReference Include="System.Drawing.Common" Version="8.0.0" />
Expand Down
2 changes: 1 addition & 1 deletion SysBot.Pokemon.ConsoleApp/SysBot.Pokemon.ConsoleApp.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<TargetFramework>net8.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Discord.Net" Version="3.14.1" />
<PackageReference Include="Discord.Net" Version="3.15.3" />
<ProjectReference Include="..\SysBot.Pokemon.Discord\SysBot.Pokemon.Discord.csproj" />
<ProjectReference Include="..\SysBot.Pokemon\SysBot.Pokemon.csproj" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
Expand Down
4 changes: 2 additions & 2 deletions SysBot.Pokemon.Discord/Commands/Bots/RaidModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -748,7 +748,7 @@ public async Task AddRaidPK([Summary("Showdown Set")][Remainder] string content)
{
raidToUpdate.PartyPK = partyPK;
await Context.Message.DeleteAsync().ConfigureAwait(false);
var embed = RPEmbed.PokeEmbed(pkm, Context.User.Username);
var embed = await RPEmbed.PokeEmbedAsync(pkm, Context.User.Username);
await ReplyAsync(embed: embed).ConfigureAwait(false);
}
else
Expand Down Expand Up @@ -795,7 +795,7 @@ public async Task AddRaidPK()
{
raidToUpdate.PartyPK = partyPK;
await Context.Message.DeleteAsync().ConfigureAwait(false);
var embed = RPEmbed.PokeEmbed(pk, Context.User.Username);
var embed = await RPEmbed.PokeEmbedAsync(pk, Context.User.Username);
await ReplyAsync(embed: embed).ConfigureAwait(false);
}
else
Expand Down
6 changes: 4 additions & 2 deletions SysBot.Pokemon.Discord/Helpers/RPEmbed.cs
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
using Discord;
using PKHeX.Core;
using System.Threading.Tasks;
using Color = Discord.Color;

namespace SysBot.Pokemon.Discord.Helpers;

public static class RPEmbed
{
public static Embed PokeEmbed(PKM pk, string username)
public static async Task<Embed> PokeEmbedAsync(PKM pk, string username)
{
var strings = GameInfo.GetStrings(GameLanguage.DefaultLanguage);
var items = strings.GetItemStrings(pk.Context, (GameVersion)pk.Version);
var formName = ShowdownParsing.GetStringFromForm(pk.Form, strings, pk.Species, pk.Context);
var itemName = items[pk.HeldItem];
(int R, int G, int B) = RaidExtensions<PK9>.GetDominantColor(RaidExtensions<PK9>.PokeImg(pk, false, false));

(int R, int G, int B) = await RaidExtensions<PK9>.GetDominantColorAsync(RaidExtensions<PK9>.PokeImg(pk, false, false));
var embedColor = new Color(R, G, B);

var embed = new EmbedBuilder
Expand Down
47 changes: 25 additions & 22 deletions SysBot.Pokemon.Discord/Helpers/ReactionService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,35 +4,38 @@
using System.Collections.Generic;
using System.Threading.Tasks;

public class ReactionService
namespace SysBot.Pokemon.Discord.Helpers
{
private readonly DiscordSocketClient _client;
private readonly Dictionary<ulong, Func<SocketReaction, Task>> _reactionActions;

public ReactionService(DiscordSocketClient client)
public class ReactionService
{
_client = client;
_reactionActions = new Dictionary<ulong, Func<SocketReaction, Task>>();
private readonly DiscordSocketClient _client;
private readonly Dictionary<ulong, Func<SocketReaction, Task>> _reactionActions;

// Subscribe to the reaction added event
_client.ReactionAdded += OnReactionAddedAsync;
}
public ReactionService(DiscordSocketClient client)
{
_client = client;
_reactionActions = new Dictionary<ulong, Func<SocketReaction, Task>>();

public void AddReactionHandler(ulong messageId, Func<SocketReaction, Task> handler)
{
_reactionActions[messageId] = handler;
}
// Subscribe to the reaction added event
_client.ReactionAdded += OnReactionAddedAsync;
}

public void RemoveReactionHandler(ulong messageId)
{
_reactionActions.Remove(messageId);
}
public void AddReactionHandler(ulong messageId, Func<SocketReaction, Task> handler)
{
_reactionActions[messageId] = handler;
}

private async Task OnReactionAddedAsync(Cacheable<IUserMessage, ulong> cachedMessage, Cacheable<IMessageChannel, ulong> cachedChannel, SocketReaction reaction)
{
if (_reactionActions.TryGetValue(reaction.MessageId, out var handler))
public void RemoveReactionHandler(ulong messageId)
{
_reactionActions.Remove(messageId);
}

private async Task OnReactionAddedAsync(Cacheable<IUserMessage, ulong> cachedMessage, Cacheable<IMessageChannel, ulong> cachedChannel, SocketReaction reaction)
{
await handler(reaction);
if (_reactionActions.TryGetValue(reaction.MessageId, out var handler))
{
await handler(reaction);
}
}
}
}
4 changes: 2 additions & 2 deletions SysBot.Pokemon.Discord/SysBot.Pokemon.Discord.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
<TargetFramework>net8.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Discord.Net" Version="3.14.1" />
<PackageReference Include="PKHeX.Core" Version="24.3.26" />
<PackageReference Include="Discord.Net" Version="3.15.3" />
<PackageReference Include="PKHeX.Core" Version="24.7.3" />
<ProjectReference Include="..\SysBot.Base\SysBot.Base.csproj" />
<ProjectReference Include="..\SysBot.Pokemon\SysBot.Pokemon.csproj" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="8.0.0" />
Expand Down
54 changes: 40 additions & 14 deletions SysBot.Pokemon.Discord/SysCord.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using Microsoft.Extensions.DependencyInjection;
using PKHeX.Core;
using SysBot.Base;
using SysBot.Pokemon.Discord.Helpers;
using System;
using System.Linq;
using System.Reflection;
Expand All @@ -16,21 +17,22 @@ namespace SysBot.Pokemon.Discord
public static class SysCordSettings
{
public static DiscordManager Manager { get; internal set; } = default!;
public static DiscordSettings Settings => Manager.Config;
public static PokeRaidHubConfig HubConfig { get; internal set; } = default!;
public static DiscordSettings? Settings => Manager.Config;
public static PokeRaidHubConfig? HubConfig { get; internal set; } = default!;
}

public sealed class SysCord<T> where T : PKM, new()
{
public static PokeBotRunner<T> Runner { get; private set; } = default!;
public static RestApplication App { get; private set; } = default!;
public static PokeBotRunner<T>? Runner { get; private set; } = default!;
public static RestApplication? App { get; private set; } = default!;

public static SysCord<T> Instance { get; private set; }
public static ReactionService ReactionService { get; private set; }
public static SysCord<T>? Instance { get; private set; }
public static ReactionService? ReactionService { get; private set; }
private readonly DiscordSocketClient _client;
private readonly DiscordManager Manager;
public readonly PokeRaidHub<T> Hub;

private const int MaxReconnectDelay = 60000; // 1 minute
private int _reconnectAttempts = 0;
// Keep the CommandService and DI container around for use with commands.
// These two types require you install the Discord.Net.Commands package.
private readonly CommandService _commands;
Expand All @@ -52,15 +54,17 @@ public SysCord(PokeBotRunner<T> runner)
_client = new DiscordSocketClient(new DiscordSocketConfig
{
LogLevel = LogSeverity.Info,
GatewayIntents = GatewayIntents.Guilds |
GatewayIntents.GuildMessages |
GatewayIntents.DirectMessages |
GatewayIntents.GuildMembers |
GatewayIntents.MessageContent |
GatewayIntents.GuildMessageReactions,
MessageCacheSize = 100,
GatewayIntents = GatewayIntents.Guilds
| GatewayIntents.GuildMessages
| GatewayIntents.DirectMessages
| GatewayIntents.MessageContent
| GatewayIntents.GuildMessageReactions
| GatewayIntents.GuildMembers,
MessageCacheSize = 500,
AlwaysDownloadUsers = true,
ConnectionTimeout = 30000,
});
_client.Disconnected += HandleDisconnect;

_commands = new CommandService(new CommandServiceConfig
{
Expand Down Expand Up @@ -132,6 +136,28 @@ private static Task Log(LogMessage msg)
_ => Console.ForegroundColor,
};

private async Task HandleDisconnect(Exception ex)
{
if (ex is GatewayReconnectException)
{
// Discord is telling us to reconnect, so we don't need to handle it ourselves
return;
}

var delay = Math.Min(MaxReconnectDelay, 1000 * Math.Pow(2, _reconnectAttempts));
await Task.Delay((int)delay);

try
{
await _client.StartAsync();
_reconnectAttempts = 0;
}
catch
{
_reconnectAttempts++;
}
}

public async Task MainAsync(string apiToken, CancellationToken token)
{
// Centralize the logic for commands into a separate method.
Expand Down
4 changes: 2 additions & 2 deletions SysBot.Pokemon.WinForms/SysBot.Pokemon.WinForms.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@
<DebugType>none</DebugType>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Discord.Net" Version="3.14.1" />
<PackageReference Include="Discord.Net" Version="3.15.3" />
<PackageReference Include="MySql.Data" Version="8.3.0" />
<PackageReference Include="PKHeX.Core" Version="24.3.26" />
<PackageReference Include="PKHeX.Core" Version="24.7.3" />
<PackageReference Include="System.Management" Version="8.0.0" />
<PackageReference Include="System.Resources.Extensions" Version="8.0.0" />
</ItemGroup>
Expand Down
Loading

0 comments on commit 94dadcb

Please sign in to comment.