Skip to content

Add Death Rate Profile Switcher - #203

Closed
Haluzer wants to merge 5 commits into
Darkbot-Plugins:mainfrom
Haluzer:death-rate-profile-switcher
Closed

Haluzer wants to merge 5 commits into
Darkbot-Plugins:mainfrom
Haluzer:death-rate-profile-switcher

Conversation

@Haluzer

@Haluzer Haluzer commented Sep 2, 2026 •

Copy link
Copy Markdown

Adds a new feature under dev.shared.haluzer.death_rate_switcher.

Watches deaths/hour on a configured "home" profile. If deaths spike past a configurable threshold in a rolling 60-minute window (PvP hunting), it switches to a configured "safe" profile for up to 60 minutes. If the death rate spikes again on the safe profile, it reverts immediately; otherwise it reverts automatically after a clean 60-minute window.

Compiled and tested against darkbot-impl 0.9.8 / DarkBot dc48506543, matching this repo's pinned versions.

This is a split-out from #200 per dm94's request for one feature per PR. The first feature (Revive Loop Watchdog) is in #201.

Summary by Sourcery

Add automatic profile switching to respond to elevated death rates and reduce exposure to PvP hunting.

New Features:

  • Add a configurable feature that monitors rolling hourly death rates and switches between selected DarkBot profiles when deaths exceed a threshold.
  • Automatically revert to the home profile after a quiet hour or immediately switch back when deaths spike on the safe profile.

Chores:

  • Register the new death-rate profile switcher in the plugin configuration.

@sourcery-ai

sourcery-ai Bot commented Sep 2, 2026 •

Copy link
Copy Markdown
Contributor

Reviewer's Guide

Introduces an enabled-by-default Death Rate Profile Switcher that monitors destruction events over a rolling hour, moves from a configured home profile to a safe profile when deaths exceed the threshold, and reverts after a clean hour or a second spike.

Sequence diagram for death-rate profile switching

sequenceDiagram
    participant Behavior as DeathRateProfileSwitcher
    participant Repair as RepairAPI
    participant Config as ConfigAPI

    loop Each behavior tick or stop
        Behavior->>Repair: isDestroyed()
        Behavior->>Config: getCurrentProfile()
        Behavior->>Behavior: trackDeathEdge()
        Behavior->>Behavior: pruneOldDeaths()
        alt Home profile and threshold reached
            Behavior->>Config: setConfigProfile(TO_PROFILE)
        else Safe profile and second spike
            Behavior->>Config: setConfigProfile(FROM_PROFILE)
        else Safe profile and 60 minutes clean
            Behavior->>Config: setConfigProfile(FROM_PROFILE)
        end
    end
Loading

File-Level Changes

Change Details Files
Adds a configurable behavior that detects death-rate spikes and switches between DarkBot configuration profiles.
  • Adds selectable source and safe profile settings plus a deaths-per-hour threshold.
  • Tracks rising-edge destruction events in a rolling 60-minute deque and prunes expired events.
  • Switches from the source profile after reaching the threshold, then returns after a quiet hour or immediately after another spike on the safe profile.
  • Registers the feature and updates plugin metadata.
src/main/java/dev/shared/haluzer/death_rate_switcher/DeathRateProfileSwitcher.java
src/main/resources/plugin.json

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 4 issues

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="src/main/java/dev/shared/haluzer/death_rate_switcher/DeathRateProfileSwitcher.java" line_range="91" />
<code_context>
+    }
+
+    private void process() {
+        if (config.FROM_PROFILE.isEmpty() || config.TO_PROFILE.isEmpty()) return;
+
+        trackDeathEdge();
+        pruneOldDeaths();
</code_context>
<issue_to_address>
**issue (bug_risk):** `process()` calls `isEmpty()` on `FROM_PROFILE` and `TO_PROFILE` without checking for null, so a configuration value represented as null by the dropdown causes every behavior tick to throw `NullPointerException`. `getText()` explicitly accepts null, so null is a supported option value in this configuration path.

**Triggers:** When either profile dropdown has a null value rather than an empty string.

**Suggested fix:** Use a null-safe check such as `String.isNullOrEmpty(...)` or normalize null values in `setConfig()`.

```suggestion
        if (config.FROM_PROFILE == null || config.FROM_PROFILE.isEmpty() || config.TO_PROFILE == null || config.TO_PROFILE.isEmpty()) return;
```
</issue_to_address>

### Comment 2
<location path="src/main/java/dev/shared/haluzer/death_rate_switcher/DeathRateProfileSwitcher.java" line_range="98" />
<code_context>
+
+        String current = configAPI.getCurrentProfile();
+
+        if (revertAt == null) {
+            if (config.FROM_PROFILE.equals(current) && recentDeaths.size() >= config.DEATHS_PER_HOUR_THRESHOLD) {
+                log("Deaths/hour reached " + recentDeaths.size() + " (threshold " + config.DEATHS_PER_HOUR_THRESHOLD +
+                        ") on '" + config.FROM_PROFILE + "'. Switching to '" + config.TO_PROFILE + "' for 60 minutes.");
+                configAPI.setConfigProfile(config.TO_PROFILE);
</code_context>
<issue_to_address>
**issue (bug_risk):** The safe-profile state is held only in the in-memory `revertAt` field. If the plugin or bot is restarted while `TO_PROFILE` is active, `revertAt` is reset to null; because the current profile is not `FROM_PROFILE`, the home-profile branch is never entered and the switcher never automatically reverts to `FROM_PROFILE`.

**Triggers:** When the bot/plugin restarts or the feature is re-created during the 60-minute safe-profile period.

**Suggested fix:** Persist the active switch state/deadline, or initialize safe-mode state when the current profile is `TO_PROFILE` and manage its deadline explicitly.

```suggestion
        if (revertAt == null && config.TO_PROFILE.equals(current)) {
            revertAt = Instant.now().plus(WINDOW);
        }

        if (revertAt == null) {
```
</issue_to_address>

### Comment 3
<location path="src/main/java/dev/shared/haluzer/death_rate_switcher/DeathRateProfileSwitcher.java" line_range="107-116" />
<code_context>
+                recentDeaths.clear();
+            }
+        } else {
+            if (recentDeaths.size() >= config.DEATHS_PER_HOUR_THRESHOLD) {
+                log("Deaths/hour reached " + recentDeaths.size() + " again while on '" + config.TO_PROFILE +
+                        "'. Being hunted there too - reverting to '" + config.FROM_PROFILE + "' immediately.");
+                configAPI.setConfigProfile(config.FROM_PROFILE);
+                revertAt = null;
+                recentDeaths.clear();
</code_context>
<issue_to_address>
**issue (bug_risk):** Once `revertAt` is set, the safe-profile branch treats deaths as safe-profile deaths and reverts to `FROM_PROFILE` without checking that the current profile is still `TO_PROFILE`. If another component or the user changes to a third profile during the safe interval, deaths on that unrelated profile still trigger the configured home-profile switch.

**Triggers:** When the active profile is changed away from `TO_PROFILE` before the safe interval expires.

**Suggested fix:** Require `config.TO_PROFILE.equals(current)` before applying safe-profile death handling or the timed revert, and reset the state when an unrelated profile is active.
</issue_to_address>

### Comment 4
<location path="src/main/java/dev/shared/haluzer/death_rate_switcher/DeathRateProfileSwitcher.java" line_range="123-126" />
<code_context>
+        }
+    }
+
+    private void trackDeathEdge() {
+        boolean destroyed = repair.isDestroyed();
+        if (destroyed && !wasDestroyed) {
+            recentDeaths.addLast(Instant.now());
+        }
+        wasDestroyed = destroyed;
</code_context>
<issue_to_address>
**issue (bug_risk):** `wasDestroyed` starts false, so the first tick after the feature becomes active records a death whenever `repair.isDestroyed()` is already true. A ship that was destroyed before the feature was enabled or before the plugin finished loading is therefore counted as a new death and can contribute to an unwarranted profile switch.

**Triggers:** When the feature first processes while the ship is already in the destroyed state.

**Suggested fix:** Initialize `wasDestroyed` from `repair.isDestroyed()` before beginning edge detection, or explicitly ignore the first observation.
</issue_to_address>

Sourcery assessment

Needs a human reviewer. 4 findings to address first, and if the profile switch is wrong, the bot can run with the alternate configuration for up to an hour and may make gameplay decisions or incur deaths before reverting. Reverting restores the selected profile, but it cannot undo consequences that occurred while the wrong profile was active.

Blocking findings: src/main/java/dev/shared/haluzer/death_rate_switcher/DeathRateProfileSwitcher.java:91, src/main/java/dev/shared/haluzer/death_rate_switcher/DeathRateProfileSwitcher.java:98, src/main/java/dev/shared/haluzer/death_rate_switcher/DeathRateProfileSwitcher.java:116, src/main/java/dev/shared/haluzer/death_rate_switcher/DeathRateProfileSwitcher.java:126


Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@dm94 dm94 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fix the errors – and this feature already exists in another publicly available plugin

@sonarqubecloud

sonarqubecloud Bot commented Sep 4, 2026

Copy link
Copy Markdown

@Haluzer

Haluzer commented Sep 4, 2026

Copy link
Copy Markdown
Author

Fix the errors – and this feature already exists in another publicly available plugin

Should be fixed now.
what other plugin does have this?
i have searched discord but i see no referance of any plugin doing anything like this

@Haluzer Haluzer closed this Sep 8, 2026
@Haluzer
Haluzer deleted the death-rate-profile-switcher branch September 8, 2026 19:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants