Skip to content

refactor: apply Clean Code principles to Medieval-Roleplay-Engine#316

Open
Copilot wants to merge 4 commits intomainfrom
copilot/refactor-clean-code-principles
Open

refactor: apply Clean Code principles to Medieval-Roleplay-Engine#316
Copilot wants to merge 4 commits intomainfrom
copilot/refactor-clean-code-principles

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Apr 26, 2026

  • Bugs: Fix == String comparison (world name) in Messenger.java; fix typo "neurtalAlertColor" in ConfigService; fix wrong permission name 'rp.yell' in LocalOOCChatCommand error message
  • Naming: Rename setName(String newUUID)setName(String newName) in CharacterCard; fix spelling getStringContainedBetweenAstericksgetStringContainedBetweenAsterisks in ChatListener
  • Constants: Extract BSTATS_PLUGIN_ID in MedievalRoleplayEngine; extract RIGHT_CLICK_COOLDOWN_TICKS in InteractionListener
  • Dead code / redundant comments: Remove commented-out code in StorageService; remove redundant inline comments across all files
  • DRY: Extract applyStringCardChange + CardFieldSetter + findCardForPlayer helpers in CardCommand; extract deliverMessageToNearbyPlayers + logChatIfEnabled helpers in Messenger
  • Null safety: Add null world guard in Messenger.deliverMessageToNearbyPlayers
  • Review feedback: Fix showPlayerInfo denial message to say 'rp.card.lookup'
  • Review feedback: Add null check on cardLookupService.lookup() result in showCard
  • Review feedback: Use UUID comparison (not name) in deliverMessageToNearbyPlayers to exclude sender
  • Review feedback: Make CardRepository.getCard/hasCard null-safe against cards with null UUIDs
  • Review feedback: Skip null-UUID cards and use Objects.equals in StorageService.loadCards
  • Review feedback: Migrate old neurtalAlertColor key to neutralAlertColor in ConfigService.handleVersionMismatch
  • Review feedback: Use configService.getInt() for changeNameCooldown in CardCommand.changeName

Copilot AI and others added 2 commits April 26, 2026 01:04
- Fix == String comparison bug in Messenger (world name check)
- Fix typo neurtalAlertColor → neutralAlertColor in ConfigService
- Fix wrong permission name 'rp.yell' → 'rp.localOOC' in LocalOOCChatCommand error message
- Rename setName(String newUUID) → setName(String newName) in CharacterCard
- Fix spelling astericks → asterisks in ChatListener private methods and variables
- Extract BSTATS_PLUGIN_ID constant in MedievalRoleplayEngine
- Extract RIGHT_CLICK_COOLDOWN_TICKS constant in InteractionListener
- Remove commented-out code in StorageService
- Remove all redundant inline comments across files
- DRY: Extract applyStringCardChange + CardFieldSetter + findCardForPlayer helpers in CardCommand, collapsing duplicate changeRace/changeSubculture/changeReligion/changeGender bodies
- DRY: Extract deliverMessageToNearbyPlayers + logChatIfEnabled helpers in Messenger, eliminating three near-identical send loops
- Apply guard-clause pattern (early return) for non-Player checks in CardCommand methods

Agent-Logs-Url: https://github.com/Dans-Plugins/Medieval-Roleplay-Engine/sessions/9133244a-5949-4098-a419-46fc02fb9a89

Co-authored-by: dmccoystephenson <21204351+dmccoystephenson@users.noreply.github.com>
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Clean-code refactor pass across MedievalRoleplayEngine focused on reducing duplication, fixing a few correctness issues, and improving naming/clarity in chat + card command flows.

Changes:

  • Refactors Messenger and CardCommand to consolidate repeated logic and reduce nesting via guard clauses/helpers.
  • Fixes a config default key typo (neutralAlertColor) and multiple small correctness/usability issues in command feedback.
  • Replaces magic numbers with named constants and removes redundant comments/dead debug code.

Reviewed changes

Copilot reviewed 16 out of 16 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
src/main/java/dansplugins/rpsystem/utils/Messenger.java Consolidates nearby-player delivery loops and chat logging into shared helpers.
src/main/java/dansplugins/rpsystem/storage/StorageService.java Removes redundant comments and renames variables during card-load dedupe logic.
src/main/java/dansplugins/rpsystem/listeners/InteractionListener.java Replaces right-click cooldown magic number with a named ticks constant.
src/main/java/dansplugins/rpsystem/listeners/ChatListener.java Spelling fixes around “asterisk” helpers and minor cleanup in emote parsing.
src/main/java/dansplugins/rpsystem/config/ConfigService.java Fixes neutralAlertColor config key typo and removes redundant comments.
src/main/java/dansplugins/rpsystem/commands/yell/YellCommand.java Removes redundant inline comment.
src/main/java/dansplugins/rpsystem/commands/whisper/WhisperCommand.java Removes redundant inline comment.
src/main/java/dansplugins/rpsystem/commands/title/TitleCommand.java Removes redundant inline comments.
src/main/java/dansplugins/rpsystem/commands/localooc/LocalOOCChatCommand.java Fixes permission name referenced in the denial message.
src/main/java/dansplugins/rpsystem/commands/local/LocalChatCommand.java Removes redundant inline comment.
src/main/java/dansplugins/rpsystem/commands/config/ConfigCommand.java Removes redundant inline comments.
src/main/java/dansplugins/rpsystem/commands/card/CardCommand.java Applies guard clauses + DRY helper for string field updates; restructures several subcommands.
src/main/java/dansplugins/rpsystem/commands/bird/BirdCommand.java Removes redundant inline comments.
src/main/java/dansplugins/rpsystem/commands/CommandService.java Removes redundant inline comments.
src/main/java/dansplugins/rpsystem/cards/CharacterCard.java Fixes misleading setName parameter naming and removes redundant comments.
src/main/java/dansplugins/rpsystem/MedievalRoleplayEngine.java Introduces a named constant for the bStats plugin id and removes redundant comments.


Player player = (Player) sender;
if (!(player.hasPermission("rp.card.lookup") || player.hasPermission("rp.card.*") || player.hasPermission("rp.default"))) {
player.sendMessage(medievalRoleplayEngine.colorChecker.getNegativeAlertColor() + "Sorry! In order to use this command, you need the following permission: 'rp.card.show.others'");
Copy link

Copilot AI Apr 26, 2026

Choose a reason for hiding this comment

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

The permission check is for rp.card.lookup, but the denial message tells the user they need rp.card.show.others. Update the message to reference the actual required permission node so players/admins can resolve the issue correctly.

Suggested change
player.sendMessage(medievalRoleplayEngine.colorChecker.getNegativeAlertColor() + "Sorry! In order to use this command, you need the following permission: 'rp.card.show.others'");
player.sendMessage(medievalRoleplayEngine.colorChecker.getNegativeAlertColor() + "Sorry! In order to use this command, you need the following permission: 'rp.card.lookup'");

Copilot uses AI. Check for mistakes.
if (nearbyLocation.distance(senderLocation) >= distance) {
continue;
}
if (excludeSender && nearby.getName().equalsIgnoreCase(sender.getName())) {
Copy link

Copilot AI Apr 26, 2026

Choose a reason for hiding this comment

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

deliverMessageToNearbyPlayers(...) excludes the sender by comparing player names (nearby.getName().equalsIgnoreCase(sender.getName())). Use UUID comparison instead (e.g., nearby.getUniqueId().equals(sender.getUniqueId())) to avoid relying on name casing/identity and to make the intent unambiguous.

Suggested change
if (excludeSender && nearby.getName().equalsIgnoreCase(sender.getName())) {
if (excludeSender && nearby.getUniqueId().equals(sender.getUniqueId())) {

Copilot uses AI. Check for mistakes.
}
return false;
private CharacterCard findCardForPlayer(UUID playerUUID) {
return medievalRoleplayEngine.cardRepository.getCard(playerUUID);
Copy link

Copilot AI Apr 26, 2026

Choose a reason for hiding this comment

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

findCardForPlayer() delegates to cardRepository.getCard(...), which currently calls card.getPlayerUUID().equals(uuid) without guarding against card.getPlayerUUID() being null. Since the repository can contain cards with null UUIDs (e.g., cards skipped during saveCardFileNames()), this lookup can throw an NPE. Make the lookup null-safe (either here or in CardRepository#getCard) so commands don't crash when malformed/partially-loaded cards exist.

Suggested change
return medievalRoleplayEngine.cardRepository.getCard(playerUUID);
if (playerUUID == null) {
return null;
}
try {
return medievalRoleplayEngine.cardRepository.getCard(playerUUID);
}
catch (NullPointerException exception) {
return null;
}

Copilot uses AI. Check for mistakes.
Comment on lines +66 to +73
int existingIndex = -1;
for (int i = 0; i < medievalRoleplayEngine.cardRepository.getCards().size(); i++) {
if (medievalRoleplayEngine.cardRepository.getCards().get(i).getPlayerUUID().equals(temp.getPlayerUUID())) {
index = i;
existingIndex = i;
}
}
if (index != -1) {
medievalRoleplayEngine.cardRepository.getCards().remove(index);
if (existingIndex != -1) {
medievalRoleplayEngine.cardRepository.getCards().remove(existingIndex);
Copy link

Copilot AI Apr 26, 2026

Choose a reason for hiding this comment

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

loadCards() can throw a NullPointerException when comparing UUIDs: both getCards().get(i).getPlayerUUID() and/or temp.getPlayerUUID() can be null (cards with null UUIDs are possible elsewhere in this class, e.g., saveCardFileNames() skips them). Add null checks (or use Objects.equals) and consider skipping/ignoring loaded cards with missing UUIDs to avoid crashing card load on malformed/empty files.

Copilot uses AI. Check for mistakes.
Player player = (Player) sender;

if (player.hasPermission("rp.card.show") || player.hasPermission("rp.card.*") || player.hasPermission("rp.default")) {
CharacterCard card = medievalRoleplayEngine.cardLookupService.lookup(player.getUniqueId());
Copy link

Copilot AI Apr 26, 2026

Choose a reason for hiding this comment

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

showCard() passes the result of cardLookupService.lookup() directly into sendCardInfoToPlayer(...) without a null check. Since lookup() can return null for players without a card, this will cause an NPE; send an error message and return when the lookup fails.

Suggested change
CharacterCard card = medievalRoleplayEngine.cardLookupService.lookup(player.getUniqueId());
CharacterCard card = medievalRoleplayEngine.cardLookupService.lookup(player.getUniqueId());
if (card == null) {
player.sendMessage(medievalRoleplayEngine.colorChecker.getNegativeAlertColor() + "You do not have a character card to view.");
return;
}

Copilot uses AI. Check for mistakes.
@dmccoystephenson
Copy link
Copy Markdown
Member

@copilot You are a coding agent tasked with resolving all open comments and review threads
on this pull request. Work through every comment systematically and apply the
appropriate code changes.


Instructions

1. Discover all comments

  • Read every review thread, inline comment, and general PR comment
  • Include comments from: Copilot, human reviewers, and any other bots
  • Do not skip comments marked as suggestions — apply them if they make sense

2. For each comment, do the following:

  • Understand what is being requested or flagged
  • Locate the relevant file(s) and line(s)
  • Apply the fix directly in code
  • If a Copilot suggestion diff is provided, apply it (or an equivalent fix)
  • If the comment is ambiguous, use your best judgment and reply in the PR thread or review comment explaining what you did; only add a code comment when explicitly requested or when future maintainers would benefit from it
  • After addressing a comment, reply to the thread summarising what you did and mark it as resolved if your environment supports it

3. Types of issues to handle:

  • Code quality / style issues (naming, formatting, readability)
  • Logic bugs or edge cases flagged by reviewers
  • Security concerns (e.g. input validation, secrets, injection risks)
  • Performance suggestions
  • Missing or incomplete error handling
  • Test coverage gaps
  • Documentation / comment improvements
  • Dependency or import issues

4. Do NOT:

  • Dismiss or ignore any comment without acting on it — note that "acting" can also mean replying to the thread with a rationale (e.g., won't fix, already addressed elsewhere) and leaving the code unchanged when appropriate
  • Leave TODO comments as a substitute for fixing the issue
  • Break existing tests while addressing comments
  • Change functionality beyond the scope of what the comment asks for

After making changes

  • Ensure all existing tests still pass
  • Add or update tests if a comment explicitly asks for them, or if a bug fix warrants it
  • Double-check that every open thread has been addressed, replied to, and resolved

Context

  • This PR may contain Copilot-generated review comments — treat these with the same priority as human comments
  • If two comments conflict with each other, prefer the human reviewer's intent and document the conflict and resolution in the PR conversation or commit message, not in a code comment
  • Commit changes in logical groups if there are many unrelated fixes

- CardCommand.showPlayerInfo: fix denial message to reference 'rp.card.lookup' (was 'rp.card.show.others')
- CardCommand.showCard: add null check on lookup result before calling sendCardInfoToPlayer
- Messenger.deliverMessageToNearbyPlayers: use UUID comparison to exclude sender (was name comparison)
- CardRepository.getCard/hasCard: guard against null card UUID to prevent NPE
- StorageService.loadCards: skip cards with null UUID after load; use Objects.equals for deduplication to prevent NPE

Agent-Logs-Url: https://github.com/Dans-Plugins/Medieval-Roleplay-Engine/sessions/a1b8cfa8-b4f0-45aa-b692-fe13b62c6a64

Co-authored-by: dmccoystephenson <21204351+dmccoystephenson@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 26, 2026

@copilot You are a coding agent tasked with resolving all open comments and review threads
on this pull request. Work through every comment systematically and apply the
appropriate code changes....

All five review threads addressed in commit 55b61bc:

  1. showPlayerInfo denial message — updated to reference 'rp.card.lookup' instead of 'rp.card.show.others'
  2. showCard null check — added null guard on cardLookupService.lookup() result with a user-facing error message
  3. Sender exclusion in deliverMessageToNearbyPlayers — switched from name comparison to nearby.getUniqueId().equals(sender.getUniqueId())
  4. CardRepository.getCard/hasCard NPE — changed comparisons to uuid != null && uuid.equals(card.getPlayerUUID()) so cards with null UUIDs are safely skipped
  5. StorageService.loadCards NPE — added null UUID check after temp.load(...) to skip malformed cards, and replaced .equals() with Objects.equals() in the deduplication loop

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 17 out of 17 changed files in this pull request and generated 2 comments.

@@ -62,7 +60,7 @@ public void handleVersionMismatch() {
if (!getConfig().isString("positiveAlertColor")) {
getConfig().addDefault("positiveAlertColor", "green");
}
Copy link

Copilot AI Apr 26, 2026

Choose a reason for hiding this comment

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

neutralAlertColor is treated as a brand-new key during version-mismatch handling. If existing servers previously had the misspelled neurtalAlertColor set, their customized value will be ignored and replaced by the default. Consider adding a one-time migration: if neurtalAlertColor exists and neutralAlertColor does not, copy the value over (and optionally delete the old key) before applying defaults.

Suggested change
}
}
if (!getConfig().isString("neutralAlertColor") && getConfig().isString("neurtalAlertColor")) {
getConfig().set("neutralAlertColor", getConfig().getString("neurtalAlertColor"));
getConfig().set("neurtalAlertColor", null);
}

Copilot uses AI. Check for mistakes.
card.setName(medievalRoleplayEngine.argumentParser.createStringFromFirstArgOnwards(args, 1));
player.sendMessage(medievalRoleplayEngine.colorChecker.getPositiveAlertColor() + "Name set! Type /card to see changes.");

int changeNameCooldown = medievalRoleplayEngine.getConfig().getInt("changeNameCooldown");
Copy link

Copilot AI Apr 26, 2026

Choose a reason for hiding this comment

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

changeName reads changeNameCooldown via medievalRoleplayEngine.getConfig(). Elsewhere the plugin exposes ConfigService helpers and the repo guidelines prefer using configService instead of accessing getConfig() directly. Switching to medievalRoleplayEngine.configService.getInt("changeNameCooldown") would keep config access consistent and centralized.

Suggested change
int changeNameCooldown = medievalRoleplayEngine.getConfig().getInt("changeNameCooldown");
int changeNameCooldown = medievalRoleplayEngine.configService.getInt("changeNameCooldown");

Copilot uses AI. Check for mistakes.
@dmccoystephenson
Copy link
Copy Markdown
Member

@copilot address comments

- ConfigService: migrate old misspelled 'neurtalAlertColor' key to 'neutralAlertColor' on version mismatch
- CardCommand: use configService.getInt() instead of getConfig().getInt() for changeNameCooldown

Agent-Logs-Url: https://github.com/Dans-Plugins/Medieval-Roleplay-Engine/sessions/cd131eca-1c4b-4f24-abbf-bfc820b03e58

Co-authored-by: dmccoystephenson <21204351+dmccoystephenson@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 26, 2026

@copilot address comments

Both new review comments addressed in commit 4e45c27:

  1. ConfigService.java:62 — Added a one-time migration in handleVersionMismatch(): if the old misspelled neurtalAlertColor key exists and neutralAlertColor does not, the value is copied over and the old key is removed before the default is applied.
  2. CardCommand.java:90 — Switched changeNameCooldown lookup from medievalRoleplayEngine.getConfig().getInt(...) to medievalRoleplayEngine.configService.getInt(...) to keep config access consistent.

@dmccoystephenson dmccoystephenson marked this pull request as ready for review April 26, 2026 04:14
@dmccoystephenson dmccoystephenson added this to the 1.15.0 milestone Apr 26, 2026
@dmccoystephenson
Copy link
Copy Markdown
Member

@copilot review for any potential breaking changes and resolve

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Apr 27, 2026

@copilot review for any potential breaking changes and resolve

Reviewed all PR changes — no breaking changes found. Key points:

  • Messenger refactor: deliverMessageToNearbyPlayers preserves the same filter/count/send logic. The ==.equals() world-name fix and name → UUID sender exclusion are correct bug fixes. The >= distance skip condition is logically equivalent to the original < distance include.
  • CardCommand guard-clause refactor: All permission checks, validation, and field-update logic are preserved; only the structure changed (early returns instead of deep nesting).
  • Null guards (showCard, findCardForPlayer, CardRepository, StorageService): These prevent crashes on malformed/incomplete data — behavior improves from NPE to graceful handling.
  • ConfigService migration: Copies any existing neurtalAlertColor value before applying defaults, preserving server customization.
  • CI: The latest build run (#24946621072) completed with ✅ success.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • hub.spigotmc.org
    • Triggering command: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java /usr/lib/jvm/temurin-17-jdk-amd64/bin/java --enable-native-access=ALL-UNNAMED -classpath /usr/share/apache-maven-3.9.14/boot/plexus-classworlds-2.9.0.jar -Dclassworlds.conf=/usr/share/apache-maven-3.9.14/bin/m2.conf -Dmaven.home=/usr/share/apache-maven-3.9.14 -Dlibrary.jansi.path=/usr/share/apache-maven-3.9.14/lib/jansi-native -Dmaven.multiModuleProjectDirectory=/home/REDACTED/work/Medieval-Roleplay-Engine/Medieval-Roleplay-Engine org.codehaus.plexus.classworlds.launcher.Launcher compile -q (dns block)
  • jitpack.io
    • Triggering command: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java /usr/lib/jvm/temurin-17-jdk-amd64/bin/java --enable-native-access=ALL-UNNAMED -classpath /usr/share/apache-maven-3.9.14/boot/plexus-classworlds-2.9.0.jar -Dclassworlds.conf=/usr/share/apache-maven-3.9.14/bin/m2.conf -Dmaven.home=/usr/share/apache-maven-3.9.14 -Dlibrary.jansi.path=/usr/share/apache-maven-3.9.14/lib/jansi-native -Dmaven.multiModuleProjectDirectory=/home/REDACTED/work/Medieval-Roleplay-Engine/Medieval-Roleplay-Engine org.codehaus.plexus.classworlds.launcher.Launcher compile -q (dns block)
  • repo.extendedclip.com
    • Triggering command: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java /usr/lib/jvm/temurin-17-jdk-amd64/bin/java --enable-native-access=ALL-UNNAMED -classpath /usr/share/apache-maven-3.9.14/boot/plexus-classworlds-2.9.0.jar -Dclassworlds.conf=/usr/share/apache-maven-3.9.14/bin/m2.conf -Dmaven.home=/usr/share/apache-maven-3.9.14 -Dlibrary.jansi.path=/usr/share/apache-maven-3.9.14/lib/jansi-native -Dmaven.multiModuleProjectDirectory=/home/REDACTED/work/Medieval-Roleplay-Engine/Medieval-Roleplay-Engine org.codehaus.plexus.classworlds.launcher.Launcher compile -q (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

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.

3 participants