Skip to content

Commit b3e8ba9

Browse files
HandyS11claude
andcommitted
fix(2a): address Copilot review on PR #13
- EventEmbedRenderer + EventsCommandHandler: throw ArgumentOutOfRangeException on an unknown MapEventKind instead of silently falling back to the chinook key (fail-fast if a future kind is added). - IRustServerConnection.GetMapMarkersAsync: correct the stale "all kinds / MarkerKind.Other" XML doc — the mapped facade only surfaces cargo/heli/chinook. - RustPlusSocketSource.AddMarkers: skip markers with null X/Y instead of substituting (0,0), which would diff as a phantom spawn/despawn at the origin. - DiscordEventChannelPoster: forward the CancellationToken via RequestOptions.CancelToken (and rethrow OperationCanceledException so a shutdown cancel isn't logged as a post failure). - running-locally.md: chinook only alerts on spawn (cargo/heli on enter+leave) — reword the #events description to match actual behavior. - EventStateStoreTests: rename the un-alerted-removal test for clarity (behavior was already correct). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 6d9b9a2 commit b3e8ba9

7 files changed

Lines changed: 30 additions & 12 deletions

File tree

docs/development/running-locally.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,8 @@ overwritten on every startup, so a normal run after the reset leaves only the cu
6464
`/workspace simulate-server name:<n> ip:<host> port:<port>` to create a server category, and
6565
`/workspace reset` to delete the whole workspace.
6666

67-
Each provisioned server category also includes an `#events` channel, which receives live alerts for
68-
**cargo ship, patrol helicopter, and chinook** arrivals and departures. No new Discord gateway intent
69-
is required — event markers are detected by polling the Rust+ socket, not the Discord gateway; only the
70-
existing **Send Messages** and **Embed Links** permissions on the provisioned channel are used.
67+
Each provisioned server category also includes an `#events` channel, which receives live alerts when a
68+
**cargo ship** or **patrol helicopter** enters or leaves the map and when a **chinook** spawns. No new
69+
Discord gateway intent is required — event markers are detected by polling the Rust+ socket, not the
70+
Discord gateway; only the existing **Send Messages** and **Embed Links** permissions on the provisioned
71+
channel are used.

src/RustPlusBot.Features.Commands/Handlers/EventsCommandHandler.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ internal sealed class EventsCommandHandler(IEventState state, ICommandLocalizer
1515
public string Name => "events";
1616

1717
/// <inheritdoc />
18+
/// <exception cref="ArgumentOutOfRangeException">A recent event has an unsupported <see cref="MapEventKind"/>.</exception>
1819
public Task<string?> ExecuteAsync(CommandContext context, CancellationToken cancellationToken)
1920
{
2021
ArgumentNullException.ThrowIfNull(context);
@@ -34,7 +35,7 @@ internal sealed class EventsCommandHandler(IEventState state, ICommandLocalizer
3435
MapEventKind.HeliEntered => "command.event.helientered",
3536
MapEventKind.HeliLeft => "command.event.helileft",
3637
MapEventKind.ChinookSpawned => "command.event.chinookspawned",
37-
_ => "command.event.chinookspawned",
38+
_ => throw new ArgumentOutOfRangeException(nameof(e), e.Kind, "Unsupported map event kind."),
3839
};
3940
return localizer.Get(key, context.Culture, grid);
4041
});

src/RustPlusBot.Features.Connections/Listening/IRustServerConnection.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,10 @@ internal interface IRustServerConnection : IAsyncDisposable
4747
/// <returns>True if the promotion succeeded; false on failure/timeout.</returns>
4848
Task<bool> PromoteToLeaderAsync(ulong steamId, TimeSpan timeout, CancellationToken cancellationToken);
4949

50-
/// <summary>Polls the current map markers (all kinds; the caller diffs by id). Throws on failure.</summary>
50+
/// <summary>Polls the current map markers the bot tracks (cargo ship, patrol helicopter, chinook), for diffing by id. Throws on failure.</summary>
5151
/// <param name="timeout">How long to wait for the response.</param>
5252
/// <param name="cancellationToken">A cancellation token.</param>
53-
/// <returns>The current markers (every kind, unclassified ones as <see cref="MarkerKind.Other"/>).</returns>
53+
/// <returns>The current cargo-ship / patrol-helicopter / chinook markers. Other marker types are not surfaced (the mapped RustPlusApi facade exposes no others the bot reasons about; crates are no longer sent by the game).</returns>
5454
Task<IReadOnlyList<MapMarkerSnapshot>> GetMapMarkersAsync(TimeSpan timeout,
5555
CancellationToken cancellationToken = default);
5656

src/RustPlusBot.Features.Connections/Listening/RustPlusSocketSource.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -399,7 +399,14 @@ private static void AddMarkers<TMarker>(
399399
{
400400
foreach (var (id, marker) in source)
401401
{
402-
into.Add(new MapMarkerSnapshot(id, kind, marker.X ?? 0f, marker.Y ?? 0f, Name: null));
402+
// Skip markers with incomplete coordinates rather than placing a phantom at the origin,
403+
// which would otherwise diff as a spurious spawn/despawn at (0, 0).
404+
if (marker.X is not { } x || marker.Y is not { } y)
405+
{
406+
continue;
407+
}
408+
409+
into.Add(new MapMarkerSnapshot(id, kind, x, y, Name: null));
403410
}
404411
}
405412

src/RustPlusBot.Features.Events/Posting/DiscordEventChannelPoster.cs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,20 @@ public async Task PostAsync(ulong channelId, Embed embed, CancellationToken canc
1717
{
1818
try
1919
{
20-
if (await client.GetChannelAsync(channelId).ConfigureAwait(false) is not ITextChannel channel)
20+
var options = new RequestOptions
21+
{
22+
CancelToken = cancellationToken
23+
};
24+
if (await client.GetChannelAsync(channelId, options).ConfigureAwait(false) is not ITextChannel channel)
2125
{
2226
return;
2327
}
2428

25-
await channel.SendMessageAsync(embed: embed).ConfigureAwait(false);
29+
await channel.SendMessageAsync(embed: embed, options: options).ConfigureAwait(false);
30+
}
31+
catch (OperationCanceledException)
32+
{
33+
throw; // Cancellation (shutdown) is not a post failure; let the relay loop unwind cleanly.
2634
}
2735
#pragma warning disable CA1031 // Broad catch: a Discord hiccup must not crash the relay.
2836
catch (Exception ex)

src/RustPlusBot.Features.Events/Rendering/EventEmbedRenderer.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ internal sealed class EventEmbedRenderer(IEventLocalizer localizer)
1212
/// <param name="evt">The event to render.</param>
1313
/// <param name="culture">The guild culture ("en"/"fr").</param>
1414
/// <returns>The built embed.</returns>
15+
/// <exception cref="ArgumentOutOfRangeException">The event kind is not a supported <see cref="MapEventKind"/>.</exception>
1516
public Embed Render(RustMapEvent evt, string culture)
1617
{
1718
ArgumentNullException.ThrowIfNull(evt);
@@ -23,7 +24,7 @@ public Embed Render(RustMapEvent evt, string culture)
2324
MapEventKind.HeliEntered => "event.heli.entered",
2425
MapEventKind.HeliLeft => "event.heli.left",
2526
MapEventKind.ChinookSpawned => "event.chinook.spawned",
26-
_ => "event.chinook.spawned",
27+
_ => throw new ArgumentOutOfRangeException(nameof(evt), evt.Kind, "Unsupported map event kind."),
2728
};
2829

2930
return new EmbedBuilder()

tests/RustPlusBot.Features.Events.Tests/State/EventStateStoreTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ public void Added_marker_becomes_active_and_carries_dimensions()
4848
}
4949

5050
[Fact]
51-
public void Removed_marker_leaves_active_even_when_unalerted()
51+
public void Removed_marker_is_cleared_from_active_even_when_unalerted()
5252
{
5353
var store = Build();
5454
// A Crate marker produces NO classified event (core-3), but is still tracked in the active set...

0 commit comments

Comments
 (0)