Subsystem 2a: live events poller + #events alerts + event !commands - #13
Merged
Conversation
…vent Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds MarkerPollInterval to ConnectionOptions (default 10s) and a PollMarkersAsync loop to ConnectionSupervisor: fetches map dimensions once on connect, polls markers on the configured interval, diffs each result against the previous snapshot, and publishes MapMarkersChangedEvent on the event bus. The first poll is a silent baseline; failed polls retain the previous snapshot without producing a spurious diff. Three integration tests verify the silent baseline, the added-marker publish, and the failed-poll snapshot-retention contract. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ript - FakeRustSocketSource: add source-level EnqueueMarkers that pre-stages marker lists and transfers them to FakeConnection at Create time, eliminating the setup race between test code and the poll loop. - FakeConnection: add per-connection ConcurrentQueue<IReadOnlyList< MapMarkerSnapshot>> with "hold last" behaviour mirroring NextHeartbeat; EnqueueMarkers on the connection available for post-connect injection. Existing MarkersResult fallback preserved for Task-2 / no-script callers. - First_marker_poll_is_a_silent_baseline: rewritten to script poll 1+2 with a CargoShip (baseline + no-change) and poll 3 with CargoShip+Heli; waits for the definite heli-added event as the race-free signal proving both baseline suppression and diff correctness. - Marker_added_on_a_later_poll_publishes_changed_event: scripted via source queue before EnsureConnectionAsync; asserts exact MapDimensions(4000,4000, 500) from the FakeConnection default. - Failed_marker_poll_retains_previous_snapshot: polls 1+2 scripted via source queue; MarkersThrow used post-event for the throw/recover cycle; hold-last means recovery sees the same CargoShip → no spurious event. - ConnectionOptions.MarkerPollInterval: init → set for IOptions binding. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Implements the pure event rendering layer: EventLocalizationCatalog with EN/FR strings for cargo, helicopter, and Chinook events; IEventLocalizer/EventLocalizer for localization; EventEmbedRenderer to build Discord embeds with grid references. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…rkspace spec Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
GridReference computed the row count from Width and never read Height/ OceanMargin — correct only because Rust maps are square. Derive each axis from its own dimension so the grid math is correct by construction. Behavior-preserving for square maps; GridReference tests unchanged. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Implements “Subsystem 2a” live events by polling Rust+ map markers per connected server, diffing marker snapshots, publishing marker-delta events, relaying classified events to a per-server #events Discord channel, and exposing in-game !cargo/!heli/!chinook/!events commands backed by an in-memory event state store.
Changes:
- Add marker polling + snapshot diff in
ConnectionSupervisor, publishingMapMarkersChangedEventon the event bus. - Introduce new
RustPlusBot.Features.Eventsproject (classifier, state store, localization/rendering, relay, hosted service, Discord poster, DI registration). - Provision and locate per-server
#eventschannels via Workspace, plus new command handlers and localization strings.
Reviewed changes
Copilot reviewed 56 out of 56 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/RustPlusBot.Features.Workspace.Tests/Locating/EventChannelLocatorTests.cs | Verifies #events locator behavior + TTL cache refresh. |
| tests/RustPlusBot.Features.Events.Tests/State/EventStateStoreTests.cs | Tests in-memory active-marker + recent-event tracking. |
| tests/RustPlusBot.Features.Events.Tests/RustPlusBot.Features.Events.Tests.csproj | Adds new Events test project. |
| tests/RustPlusBot.Features.Events.Tests/Rendering/EventLocalizerTests.cs | Tests event string localization (EN/FR, fallback, region normalization). |
| tests/RustPlusBot.Features.Events.Tests/Rendering/EventEmbedRendererTests.cs | Tests embed rendering output for key event kinds. |
| tests/RustPlusBot.Features.Events.Tests/Relaying/EventRelayTests.cs | Tests relay posting behavior + state updates. |
| tests/RustPlusBot.Features.Events.Tests/Formatting/GridReferenceTests.cs | Tests grid formatting (dims + raw coord fallback). |
| tests/RustPlusBot.Features.Events.Tests/EventRegistrationTests.cs | Validates DI registrations resolve for Events feature. |
| tests/RustPlusBot.Features.Events.Tests/Classifying/MarkerEventClassifierTests.cs | Tests delta→domain-event classification rules. |
| tests/RustPlusBot.Features.Connections.Tests/Fakes/FakeRustSocketSource.cs | Extends fake socket with scripted marker polling + dims. |
| tests/RustPlusBot.Features.Connections.Tests/ConnectionSupervisorTests.cs | Adds supervisor tests for baseline suppression, marker diffs, failure retention. |
| tests/RustPlusBot.Features.Commands.Tests/Handlers/EventHandlersTests.cs | Tests new !cargo/!heli/!chinook/!events handlers. |
| tests/RustPlusBot.Features.Commands.Tests/CommandRegistrationTests.cs | Updates command DI registration expectations (+4 handlers). |
| tests/RustPlusBot.Abstractions.Tests/Connections/MapMarkerSnapshotTests.cs | Adds tests for new marker/dimension abstractions. |
| src/RustPlusBot.Host/RustPlusBot.Host.csproj | References new Events feature project from host. |
| src/RustPlusBot.Host/Program.cs | Wires Events into host + validates marker poll interval option. |
| src/RustPlusBot.Features.Workspace/WorkspaceServiceCollectionExtensions.cs | Registers IEventChannelLocator in Workspace DI. |
| src/RustPlusBot.Features.Workspace/WorkspaceKeys.cs | Adds WorkspaceChannelKeys.ServerEvents. |
| src/RustPlusBot.Features.Workspace/Specs/ServerWorkspaceSpecProvider.cs | Provisions per-server #events channel spec. |
| src/RustPlusBot.Features.Workspace/Locating/IEventChannelLocator.cs | Defines #events channel locator interface. |
| src/RustPlusBot.Features.Workspace/Locating/EventChannelLocator.cs | Implements TTL-cached lookup of provisioned #events channels. |
| src/RustPlusBot.Features.Workspace/Localization/LocalizationCatalog.cs | Adds localized channel name for #events. |
| src/RustPlusBot.Features.Events/State/IEventState.cs | Public read seam for active markers + recent events. |
| src/RustPlusBot.Features.Events/State/EventStateStore.cs | In-memory per-(guild,server) active set + bounded recent ring. |
| src/RustPlusBot.Features.Events/State/ActiveMarker.cs | Record for active marker state (coords, dims, seen time). |
| src/RustPlusBot.Features.Events/RustPlusBot.Features.Events.csproj | Adds new Events feature project + references. |
| src/RustPlusBot.Features.Events/Rendering/IEventLocalizer.cs | Localizer interface for event strings. |
| src/RustPlusBot.Features.Events/Rendering/EventLocalizer.cs | Implements culture normalization + fallback localization. |
| src/RustPlusBot.Features.Events/Rendering/EventLocalizationCatalog.cs | Adds EN/FR event string catalog. |
| src/RustPlusBot.Features.Events/Rendering/EventEmbedRenderer.cs | Renders RustMapEvent as a Discord embed. |
| src/RustPlusBot.Features.Events/Relaying/EventRelay.cs | Classifies deltas, updates state, posts embeds to #events. |
| src/RustPlusBot.Features.Events/Posting/IEventChannelPoster.cs | Poster abstraction for Discord channel posting. |
| src/RustPlusBot.Features.Events/Posting/DiscordEventChannelPoster.cs | Discord.Net-backed embed posting implementation. |
| src/RustPlusBot.Features.Events/Hosting/EventsHostedService.cs | Hosted service consuming marker + connection status events. |
| src/RustPlusBot.Features.Events/Formatting/GridReference.cs | World-coords → grid reference (or raw coords). |
| src/RustPlusBot.Features.Events/EventServiceCollectionExtensions.cs | Registers Events services + hosted service in DI. |
| src/RustPlusBot.Features.Events/Classifying/RustMapEvent.cs | Domain event record for classified map events. |
| src/RustPlusBot.Features.Events/Classifying/MarkerEventClassifier.cs | Delta classifier for cargo/heli/chinook events. |
| src/RustPlusBot.Features.Events/Classifying/MapEventKind.cs | Enum for supported live map event types. |
| src/RustPlusBot.Features.Connections/Supervisor/ConnectionSupervisor.cs | Adds marker poll loop + publishes MapMarkersChangedEvent. |
| src/RustPlusBot.Features.Connections/Listening/RustPlusSocketSource.cs | Implements marker polling + map dimension fetch from RustPlusApi. |
| src/RustPlusBot.Features.Connections/Listening/IRustServerConnection.cs | Extends socket contract with marker/dimension APIs. |
| src/RustPlusBot.Features.Connections/ConnectionOptions.cs | Adds MarkerPollInterval option. |
| src/RustPlusBot.Features.Commands/RustPlusBot.Features.Commands.csproj | References Events project for new command handlers. |
| src/RustPlusBot.Features.Commands/Localization/CommandLocalizationCatalog.cs | Adds EN/FR strings for new event commands. |
| src/RustPlusBot.Features.Commands/Handlers/HeliCommandHandler.cs | Adds !heli handler. |
| src/RustPlusBot.Features.Commands/Handlers/EventsCommandHandler.cs | Adds !events handler. |
| src/RustPlusBot.Features.Commands/Handlers/ChinookCommandHandler.cs | Adds !chinook handler. |
| src/RustPlusBot.Features.Commands/Handlers/CargoCommandHandler.cs | Adds !cargo handler. |
| src/RustPlusBot.Features.Commands/CommandServiceCollectionExtensions.cs | Registers new command handlers in DI. |
| src/RustPlusBot.Abstractions/Events/MapMarkersChangedEvent.cs | Adds event-bus payload for marker delta publication. |
| src/RustPlusBot.Abstractions/Connections/MarkerKind.cs | Adds marker-kind enum used by polling/classification. |
| src/RustPlusBot.Abstractions/Connections/MapMarkerSnapshot.cs | Adds marker snapshot abstraction. |
| src/RustPlusBot.Abstractions/Connections/MapDimensions.cs | Adds map dimensions abstraction for grid rendering. |
| RustPlusBot.slnx | Includes new Events projects in solution. |
| docs/development/running-locally.md | Documents #events channel behavior for local runs. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- 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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Subsystem 2a — Live Events
First slice of subsystem 2 (Map + live events). Ships the live event layer: a per-connected-server map-marker poller that detects when a cargo ship, patrol helicopter, or chinook enters/leaves the map, alerts each to a per-server
#eventsDiscord channel, and answers four in-game!commands. No image rendering — that (plus/map, the#infomap field) is deferred to 2b.What's in
ConnectionSupervisor's connected window: fetches map dimensions once on connect, pollsGetMapMarkerseveryConnectionOptions.MarkerPollInterval(default 10s), diffs snapshots by marker id, and publishesMapMarkersChangedEventon the bus. First poll after connect is a silent baseline; a failed poll retains the previous snapshot (no spurious despawn/respawn).RustPlusBot.Features.Eventsproject: a pureMarkerEventClassifier(delta → domain events), a pureGridReference(world coords →"D7"), an in-memoryEventStateStore(active markers + a bounded recent-event ring, cleared on disconnect) behind a publicIEventStateseam, anEventRelaythat posts one EN/FR embed per event, and a thinEventsHostedService.#eventschannel provisioned by Workspace (read-only) + a publicIEventChannelLocator.!cargo,!heli,!chinook,!events(readIEventState).Scope note — CORE-3, not core-4
The design originally included Locked Crate, but the current Rust game no longer sends crate map markers, and the mapped
RustPlusApi.Data.MapMarkersfacade has no crate bucket. So the shipped set is cargo / patrol helicopter / chinook.MarkerKind.Crateis retained as an unused enum member for forward-compat. Crate/oil-rig detection (which would need an unverified CH47-based workaround) and Explosion events are deferred.Deferred to 2b
Base-map image rendering (ImageSharp),
/mapslash variants,#infomap image, Explosion events, crate/oil-rig (!small/!large), event-history persistence.Verification
-warnaserror);dotnet jb cleanupcodeclean.Features.Events.Tests;Features.Commands+4;Workspace+3 locator).#eventsreuses the existingProvisionedChannelreconciliation. No new gateway intent (markers arrive over the Rust+ socket).RustPlusSocketSourcemarker/dimension mapping is the single untested integration shim (consistent with prior slices).Built subagent-driven (per-task TDD + spec/quality reviews) with a final whole-branch review = READY TO MERGE.
🤖 Generated with Claude Code