Add Alert node for out-of-game notifications - #78
Open
Gaetarra wants to merge 3 commits into
Open
Conversation
Automation runs while the player is tabbed out or AFK, so a graph needs a way to reach someone who is not looking at the screen. Send Message only reaches chat, which nobody sees while a farm runs unattended. Alert has two modes. Play Sound takes any vanilla sound id plus a volume, and plays it through the sound manager with SimpleSoundInstance.forUI so the alert is not attenuated by where the player happens to be standing. Send Webhook POSTs the node's text to an endpoint configured in settings, which is what reaches a phone when nobody is at the machine. Non-obvious calls made here: - The node completes its future as soon as the notification is dispatched rather than awaiting the response. An alert must never stall the graph it is reporting on, so webhook failures are logged rather than propagated. - Webhook sends are rate limited to one per three seconds. An Alert placed inside Forever would otherwise hammer the endpoint, and Discord kills webhooks that are hammered. - Payload shape is chosen by host: Discord needs a JSON body, ntfy takes the raw text. This is a deliberate two-case heuristic rather than another mode. - The URL is validated once in SettingsManager.sanitizeWebhookUrl and https is required. It is a user-supplied string driving an outbound request from the game client, so it is checked at the boundary instead of at each call site. - Alert reuses the multi-line text fields that Send Message and Calculate already have, by joining NodeTextContent, so message text supports runtime variable interpolation with no new UI. - Toast was considered and left out: it only helps someone already watching the screen, which is not the case this node exists for. Not yet wired: the webhook URL has no settings-popup field, so it is set by editing pathmind/settings.json. That control lives in a Stonecutter source file and is left for a follow-up so this change stays reviewable. Verified: Fabric compiles on 26.2 and NeoForge on 26.1.2, and the generated mc26 source is byte-identical to the authored file, so no string transform was needed. The escaping and URL-validation logic was checked separately against the same inputs as NodeAlertTest. Not verified locally: :common:test and the 1.21.x targets, which need a JDK 21 toolchain; CI covers both. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
NodeMode has getModesForNodeType, not getAvailableModesForNodeType, so the assertion would not have compiled. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Alert shipped unusable: it rendered only its message field, so the Sound and Volume parameters were invisible and there was no way to switch to Send Webhook. Its mode names also displayed as raw keys. Two causes, both missed because the editor has no automated coverage: - NodeGraph.rendersInlineParameters decides whether a node draws its parameter strip, and the mode selector is drawn inside that strip. It is true only for parameter nodes and for types tagged RENDER_INLINE_PARAMETERS. Alert was neither, so the strip that would have held both never rendered. - NodeMode.getDisplayName ignores the display strings in the enum constructor and builds "pathmind.node.mode.<name>" as a translation key. Without lang entries the modes rendered as pathmind.node.mode.alert_sound. Verified in a dev client: the mode selector opens, Sound and Volume are editable, and both modes read as English. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This was referenced Aug 29, 2026
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.
What This Changes
Adds an
ALERTnode so a graph can reach someone who is not looking at thescreen. Automation runs while the player is tabbed out or AFK, and
MESSAGEonly reaches chat, which nobody sees while a farm runs unattended.
Two modes:
manager with
SimpleSoundInstance.forUIso the alert is not attenuated bywhere the player is standing.
settings. This is the mode that reaches a phone.
Verification
./gradlew :common:test -Pmc_version=1.21.11verifyCompatibilityManifest verifyCompatibilityStructure verifyBuildGenerationRouting1.21/1.21.8/1.21.10/1.21.11common/src/main, but that is an argument, not a check.26.1/26.2(-p mc26, both loaders)26.1.2. NeoForge on26.2fails withCould not find net.neoforged:neoforge:unsupported— the manifest listsneoforge=unsupportedfor26.2and themc26build reads it without the root build'stakeUnlessfilter. Pre-existing, unrelated to this change.Notes For Review
Non-obvious decisions:
rather than awaiting the HTTP response. An alert must never stall the graph it
is reporting on, so webhook failures are logged instead of propagated.
Foreverwould otherwise hammer the endpoint, and Discord kills webhooks thatare hammered.
raw text. A deliberate two-case heuristic rather than a third node mode; there
is a comment marking it as the thing to revisit if a third shape shows up.
SettingsManager.sanitizeWebhookUrl, andhttps is required. It is a user-supplied string driving an outbound request
from the game client, so it is checked at the boundary rather than at each
call site.
NodeAlertTestcovers the rejection cases.MESSAGEandCALCULATEalready have, by joining
NodeTextContent, so message text supports runtimevariable interpolation with no new UI.
the screen, which is not the case this node exists for.
Out of scope, deliberately:
pathmind/settings.json. That control lives in a Stonecutter source file with15 conditional regions; adding it here would have made this much harder to
review. Happy to follow up.
Worth a look by hand:
NodeCataloggainedNodeType.ALERTin theRENDER_INLINE_PARAMETERStag. Without itNodeGraph.rendersInlineParametersis false, the parameter strip is never drawn, and the mode selector that lives
inside that strip is unreachable. That is a general trap — several existing
node types have modes with no way to select them — but this PR only fixes it
for
ALERT.