Skip to content

Add player lookup and admin head management features#57

Open
benrobson wants to merge 8 commits intostagingfrom
claude/plugin-review-heads-feature-5hKF0
Open

Add player lookup and admin head management features#57
benrobson wants to merge 8 commits intostagingfrom
claude/plugin-review-heads-feature-5hKF0

Conversation

@benrobson
Copy link
Copy Markdown
Member

Summary

This PR enhances the PlayerHeadHunt plugin with player lookup capabilities and improved admin commands for managing player head collections. It refactors data access patterns to use a single source of truth and adds new utility methods for querying player statistics.

Key Changes

  • New Player Lookup Features

    • Added foundHeadsCountByName() method to query head counts by player name
    • Added clearHeadsByName() method to clear heads for a named player (works from console)
    • Enhanced /heads command to support optional player argument: /heads [player]
    • Enhanced /debugheadhunt clearheads to support clearing heads for named players: /debugheadhunt clearheads [player]
  • Data Access Refactoring

    • Refactored foundHeadsAlreadyCount() to iterate through all players instead of assuming a flat "heads" list, making it consistent with actual data structure
    • Removed duplicate data management logic from HeadWorldController.playerCollectedHead() - now delegates to HeadQuery.insertCollectedHead() as single source of truth
    • Fixed comparison logic in hasAlreadyCollectedHead() to use Objects.equals() for safer integer comparisons
  • New Utility Methods

    • Added getLoadedPlayerCount() to retrieve total number of loaded player profiles
    • Added getTotalHeadsCollectedAcrossAllPlayers() to get aggregate head collection statistics
    • Added startup logging to display loaded player count and total heads collected
  • UI/UX Improvements

    • Enhanced leaderboard console output with formatted header and better display of hunter names and head counts
    • Added new chat response method targetPlayerHeadCountResponse() for displaying other players' head counts
    • Added validation to prevent players from collecting heads after reaching the total head limit
    • Improved command usage messages and permission checks
  • Bug Fixes

    • Fixed permission check logic in debugheadhunt command (changed OR to AND for proper permission validation)
    • Fixed spacing issue in pom.xml shade plugin configuration
  • Dependencies

    • Updated Maven compiler plugin to 3.13.0
    • Updated Maven shade plugin to 3.5.3
    • Updated Lombok to 1.18.36
    • Updated SnakeYAML to 2.3
    • Updated HttpClient5 to 5.4.1

Notable Implementation Details

  • The refactored foundHeadsAlreadyCount() now properly iterates through the player data structure, checking each player's collected heads against the specified coordinates
  • Admin commands now work from both console and in-game, with proper feedback for online/offline players
  • The plugin now logs startup statistics to help administrators verify data loading

https://claude.ai/code/session_01JyfubfoVpre8dsRWa3K6Vx

Bugs fixed:
- foundHeadsAlreadyCount always returned 0 (looked for non-existent top-level
  "heads" key), causing every player to see "You are the first to find this egg"
  — now iterates all player records correctly
- Double head recording: HeadWorldController.playerCollectedHead manually wrote
  to the YAML list and then also called insertCollectedHead, recording each
  collection twice; removed the duplicate write
- Integer == comparison in hasAlreadyCollectedHead and HeadWorldController used
  reference equality, which fails for coordinates outside JVM cache range
  (-128..127) — switched to Objects.equals()
- debugheadhunt permission check used || instead of &&, requiring players to
  have BOTH the permission node and OP simultaneously; fixed to &&
- SKINSMAX was 9 but config contained 10 skins (indices 0-9), leaving skin 9
  unreachable; corrected to 10
- Leading space in maven-shade-plugin shadedPattern removed
- Version mismatch between pom.xml (1.1.1) and plugin.yml (1.2.0) — aligned
  pom.xml to 1.2.0

New features:
- On startup, logs loaded player profile count and total heads collected
- /heads [player] now accepts an optional player name so any sender (including
  console) can look up a specific player's egg count
- /debugheadhunt clearheads [player] now accepts an optional player name,
  allowing console and admins to clear another player's heads; also updates
  their helmet and scoreboard if they are online
- /leaderboard console output now shows "1. PlayerName - N heads" instead of
  the raw record toString()
- Players who have already collected all available eggs are now blocked from
  continuing to collect more (LANG.HEAD.ALLHEADSCOLLECTED config key added)

Dependency updates:
- maven-compiler-plugin 3.8.1 → 3.13.0
- maven-shade-plugin 3.4.1 → 3.5.3
- lombok 1.18.30 → 1.18.36
- snakeyaml 2.0 → 2.3
- httpclient5 5.2 → 5.4.1

https://claude.ai/code/session_01JyfubfoVpre8dsRWa3K6Vx
@benrobson benrobson requested a review from JaedanC as a code owner March 31, 2026 19:53
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 31, 2026

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b354910b-73de-4912-9c00-14b076c1b182

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/plugin-review-heads-feature-5hKF0

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

claude added 7 commits April 1, 2026 21:21
Inserts copper helmet into the progression between leather (25) and
chainmail (100). Requires Minecraft 1.21.2+ (Material.COPPER_HELMET).

https://claude.ai/code/session_01JyfubfoVpre8dsRWa3K6Vx
Copper armor was added in Minecraft 1.21.2; Material.COPPER_HELMET does
not exist in the 1.20.4 API, causing a compilation error. Updated the
Paper API dependency to 1.21.4-R0.1-SNAPSHOT and raised api-version in
plugin.yml to 1.21 accordingly.

https://claude.ai/code/session_01JyfubfoVpre8dsRWa3K6Vx
Previously, helmets were mapped via separate keys (LEATHERHELMET: 25,
CHAINMAILHELMET: 100, etc.) which required keeping two separate lists in
sync. Each milestone entry can now carry an optional 'helmet' field, so
adding or moving a milestone is a single edit in one place.

Config format change:
  MINOR / MAJOR entries are either a plain number (no helmet) or a map:
    - count: 50
      helmet: LEATHER_HELMET

The parser in PluginConfig.parseMilestoneEntry() handles both forms and
logs a warning for any unrecognised Material name rather than crashing.

https://claude.ai/code/session_01JyfubfoVpre8dsRWa3K6Vx
Milestone counts used to be hardcoded values that had to be updated
manually whenever eggs were added or removed. They are now stored as
percentages of the total egg count and recomputed automatically every
time countHeadsInRegion() updates the total (i.e. on startup and via
/debugheadhunt countheads).

Config format change — each MILESTONES entry is now a percentage:
  Plain number:  - 8.0          (8% of total, no helmet)
  With helmet:   - percentage: 25.0
                   helmet: IRON_HELMET

PluginConfig.recomputeMilestones(int) clears and repopulates the
headMilestones map in-place, so HeadFindEvent's reference to the map
automatically reflects the updated counts without re-registration.
The computed milestone counts are logged to console after each scan.

https://claude.ai/code/session_01JyfubfoVpre8dsRWa3K6Vx
When FEATURE.DISABLEFLIGHT is TRUE the plugin:
- Strips allowFlight and isFlying from every non-exempt player on join,
  overriding any grant made by another plugin or the server before the
  join event fires (EventPriority.HIGHEST)
- Cancels PlayerToggleFlightEvent mid-session for the same player set,
  as a secondary guard against plugins granting flight after join
- Sends LANG.FLIGHT.DISABLED message when flight is blocked

Exempt from restriction:
  - Ops
  - Players with playerheadhunt.flight.exempt permission
  - Creative and Spectator mode players

New files / changes:
  events/HeadHunterFlightControl.java  — new listener + enforceFlight()
  events/HeadHunterOnJoin.java         — calls enforceFlight() on join
  PluginConfig.java                    — flightDisabledFeatureEnabled,
                                         langFlightDisabled fields
  config.yml                           — FEATURE.DISABLEFLIGHT, LANG.FLIGHT
  plugin.yml                           — playerheadhunt.flight.exempt perm

https://claude.ai/code/session_01JyfubfoVpre8dsRWa3K6Vx
saveDefaultConfig() only writes the file when it doesn't exist, so
servers upgrading from an older version never received new config keys
(e.g. LANG.FLIGHT.DISABLED). Objects.requireNonNull() then threw NPE
before console was even assigned, causing a second NPE in onDisable.

Fix: call getConfig().options().copyDefaults(true) + saveConfig() after
saveDefaultConfig() so any missing keys are merged from the bundled
default before PluginConfig reads them.

Also guard onDisable against a null console so startup failures don't
produce a second confusing stack trace.

https://claude.ai/code/session_01JyfubfoVpre8dsRWa3K6Vx
Root cause: calling saveConfig() with copyDefaults(true) on startup tried
to merge new structured MILESTONES defaults (percentage maps) into the
server's existing config (plain integer lists), producing invalid YAML.
On the next restart Bukkit could not parse the corrupt file, causing
getString() to return null for keys that had always existed (e.g.
LANG.HEAD.HEADCOLLECTIONMILESTONEREACHED), which then hit requireNonNull.

Fixes:
- Remove saveConfig() from onEnable entirely. copyDefaults(true) alone is
  sufficient: Bukkit's getConfig() already sets the bundled config.yml as
  the defaults source, so any key absent from the server file is served
  from the in-memory defaults without touching disk.
- Replace every Objects.requireNonNull(config.getString(...)) in
  PluginConfig with a lang() helper that calls config.getString() directly
  (which falls back to bundled defaults automatically) and logs a warning
  + returns "" if somehow still null, rather than crashing the server.

Note: if your config.yml was already corrupted by the previous build,
delete plugins/PlayerHeadHunt/config.yml and restart to regenerate it.

https://claude.ai/code/session_01JyfubfoVpre8dsRWa3K6Vx
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