Skip to content

GH-218 Add CrystalPVP expansion #218

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
Open

GH-218 Add CrystalPVP expansion #218

wants to merge 10 commits into from

Conversation

CitralFlo
Copy link
Member

Add support for crystal pvp and anchor pvp.

Took 1 hour 23 minutes
Took 1 hour 23 minutes

Took 1 hour 33 minutes
# Conflicts:
#	eternalcombat-plugin/src/main/java/com/eternalcode/combat/CombatPlugin.java

Took 24 minutes
Took 6 minutes
Copy link

coderabbitai bot commented Apr 22, 2025

Walkthrough

This update adds new features to handle combat tagging when players interact with Ender Crystals and respawn anchors. It introduces a new configuration section to toggle tagging from these interactions. Two new listener classes track damage caused by crystals and respawn anchors, tagging both the attacker and the victim during combat. Supporting classes manage metadata on crystals and anchors to remember who caused the damage. A utility class for simple method invocation via reflection is also included. The combat tag reason enum gains a new value for crystal-related tags. Finally, the plugin setup is updated to register the new listeners and use the new configuration settings.

Tip

⚡️ Faster reviews with caching
  • CodeRabbit now supports caching for code and dependencies, helping speed up reviews. This means quicker feedback, reduced wait times, and a smoother review experience overall. Cached data is encrypted and stored securely. This feature will be automatically enabled for all accounts on May 16th. To opt out, configure Review - Disable Cache at either the organization or repository level. If you prefer to disable all data retention across your organization, simply turn off the Data Retention setting under your Organization Settings.

Enjoy the performance boost—your workflow just got faster.


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 188ab9b and 4c8528d.

📒 Files selected for processing (1)
  • eternalcombat-plugin/src/main/java/com/eternalcode/combat/crystalpvp/CrystalPvpConstants.java (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • eternalcombat-plugin/src/main/java/com/eternalcode/combat/crystalpvp/CrystalPvpConstants.java
✨ Finishing Touches
  • 📝 Generate Docstrings

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@CitralFlo CitralFlo requested a review from Rollczi April 22, 2025 19:08
@CitralFlo CitralFlo changed the title Add CrystalPVP expansion GH-218 Add CrystalPVP expansion Apr 22, 2025
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (13)
eternalcombat-plugin/src/main/java/com/eternalcode/combat/config/implementation/PluginConfig.java (1)

74-78: Add descriptive comments for the crystalPvp configuration section.

The comment block for the new crystalPvp configuration is empty. All other configuration sections include helpful explanations of their purpose.

Add descriptive comments similar to other sections:

 @Comment({
     " ",
-
+    "# Settings related to crystal PvP and respawn anchor combat tagging.",
+    "# Configure whether players should be tagged when damaged by crystal or respawn anchor explosions."
 })
 public CrystalPvpSettings crystalPvp = new CrystalPvpSettings();
eternalcombat-api/src/main/java/com/eternalcode/combat/fight/event/CauseOfTag.java (1)

21-24: Enhance the JavaDoc comment for the CRYSTAL enum value.

The current comment is brief compared to other enum values which provide more context.

Improve the comment to match the style of other enum values:

 /**
- * Crystal explosion caused the tag.
+ * The player was tagged in combat due to damage from an End Crystal or Respawn Anchor explosion
+ * caused by another player.
  */
 CRYSTAL,
eternalcombat-plugin/src/main/java/com/eternalcode/combat/util/ReflectUtil.java (2)

12-12: Fix extra space in method signature.

There's an extra space between static and <T>.

-    public static  <T> T invokeMethod(Object object, String name) {
+    public static <T> T invokeMethod(Object object, String name) {

11-21: Consider providing more specific exception handling.

The current implementation wraps all reflection exceptions into a generic RuntimeException, which makes it hard to diagnose issues.

Consider creating a more specific exception class or preserving the original exception message:

 @SuppressWarnings("unchecked")
 public static <T> T invokeMethod(Object object, String name) {
     try {
         if (object == null) {
             return null;
         }
         Method method = object.getClass().getDeclaredMethod(name);
         method.setAccessible(true);
         return (T) method.invoke(object);
     }
     catch (InvocationTargetException | NoSuchMethodException | IllegalAccessException exception) {
-        throw new RuntimeException(exception);
+        throw new RuntimeException("Failed to invoke method " + name + " on " + object.getClass().getName(), exception);
     }
 }
eternalcombat-plugin/src/main/java/com/eternalcode/combat/crystalpvp/CrystalPvpSettings.java (2)

8-9: Comment formatting is inconsistent with line 12

There's a space after the '#' in this comment, but not in the comment on line 12.

-    @Comment({"# Should player be tagged when damaged from crystal explosion set by other player"})
+    @Comment({"#Should player be tagged when damaged from crystal explosion set by other player"})

12-13: Comment wording could be improved

The comment has a small grammar issue and could be phrased more clearly.

-    @Comment({"#Should player be tagged when damaged from respawn anchor explosion set by other player"})
+    @Comment({"#Should player be tagged when damaged from respawn anchor explosion set by other player"})
eternalcombat-plugin/src/main/java/com/eternalcode/combat/crystalpvp/CrystalMetadata.java (1)

10-32: Consider adding JavaDoc comments

The class is well-structured, but adding JavaDoc comments would make it clearer to other developers.

eternalcombat-plugin/src/main/java/com/eternalcode/combat/crystalpvp/EndCrystalListener.java (2)

25-25: Use a more descriptive metadata key

The metadata key "eternalcombat:wao" doesn't clearly describe its purpose. Consider using something more descriptive.

-    private static final String CRYSTAL_METADATA = "eternalcombat:wao";
+    private static final String CRYSTAL_METADATA = "eternalcombat:crystal_damager";

57-62: Simplify metadata extraction

The stream operations to extract metadata can be simplified for better readability.

-            Optional<UUID> optionalUniqueId = damager
-                    .stream()
-                    .filter(source -> source instanceof CrystalMetadata)
-                    .map(meta -> (CrystalMetadata) meta)
-                    .findFirst()
-                    .flatMap(metadata -> metadata.getDamager());
+            Optional<UUID> optionalUniqueId = damager.stream()
+                    .filter(CrystalMetadata.class::isInstance)
+                    .map(CrystalMetadata.class::cast)
+                    .findFirst()
+                    .flatMap(CrystalMetadata::getDamager);
eternalcombat-plugin/src/main/java/com/eternalcode/combat/crystalpvp/RespawnAnchorListener.java (4)

29-29: Use a more descriptive metadata key

Similar to the EndCrystalListener, the metadata key should be more descriptive.

-    private static final String ANCHOR_METADATA = "eternalcombat:wao";
+    private static final String ANCHOR_METADATA = "eternalcombat:anchor_damager";

90-92: Add space consistency

There's an extra space after null in the if condition.

-        if (state == null ) {
+        if (state == null) {

99-103: Simplify metadata extraction

Similar to EndCrystalListener, this stream operation could be simplified.

-        Optional<UUID> damagerOptional = state.getMetadata(ANCHOR_METADATA).stream()
-            .filter(source -> source instanceof CrystalMetadata)
-            .map(meta -> (CrystalMetadata) meta)
-            .findFirst()
-            .flatMap(metadata -> metadata.getDamager());
+        Optional<UUID> damagerOptional = state.getMetadata(ANCHOR_METADATA).stream()
+            .filter(CrystalMetadata.class::isInstance)
+            .map(CrystalMetadata.class::cast)
+            .findFirst()
+            .flatMap(CrystalMetadata::getDamager);

56-82: Consider simplifying conditions and reducing duplication

The logic for setting metadata has repeated code blocks for different conditions.

Consider extracting the common code to a helper method:

private void setAnchorMetadata(Block block, Player player) {
    block.setMetadata(
        ANCHOR_METADATA,
        new CrystalMetadata(this.plugin, player.getUniqueId())
    );
}

Then use this helper method in each condition.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a8b48b8 and 3f117f3.

📒 Files selected for processing (8)
  • eternalcombat-api/src/main/java/com/eternalcode/combat/fight/event/CauseOfTag.java (1 hunks)
  • eternalcombat-plugin/src/main/java/com/eternalcode/combat/CombatPlugin.java (2 hunks)
  • eternalcombat-plugin/src/main/java/com/eternalcode/combat/config/implementation/PluginConfig.java (2 hunks)
  • eternalcombat-plugin/src/main/java/com/eternalcode/combat/crystalpvp/CrystalMetadata.java (1 hunks)
  • eternalcombat-plugin/src/main/java/com/eternalcode/combat/crystalpvp/CrystalPvpSettings.java (1 hunks)
  • eternalcombat-plugin/src/main/java/com/eternalcode/combat/crystalpvp/EndCrystalListener.java (1 hunks)
  • eternalcombat-plugin/src/main/java/com/eternalcode/combat/crystalpvp/RespawnAnchorListener.java (1 hunks)
  • eternalcombat-plugin/src/main/java/com/eternalcode/combat/util/ReflectUtil.java (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (4)
eternalcombat-plugin/src/main/java/com/eternalcode/combat/config/implementation/PluginConfig.java (1)
eternalcombat-plugin/src/main/java/com/eternalcode/combat/crystalpvp/CrystalPvpSettings.java (1)
  • CrystalPvpSettings (6-14)
eternalcombat-plugin/src/main/java/com/eternalcode/combat/CombatPlugin.java (2)
eternalcombat-plugin/src/main/java/com/eternalcode/combat/crystalpvp/RespawnAnchorListener.java (1)
  • RespawnAnchorListener (23-124)
eternalcombat-plugin/src/main/java/com/eternalcode/combat/crystalpvp/EndCrystalListener.java (1)
  • EndCrystalListener (19-81)
eternalcombat-plugin/src/main/java/com/eternalcode/combat/crystalpvp/EndCrystalListener.java (1)
eternalcombat-plugin/src/main/java/com/eternalcode/combat/config/implementation/PluginConfig.java (1)
  • PluginConfig (14-136)
eternalcombat-plugin/src/main/java/com/eternalcode/combat/crystalpvp/RespawnAnchorListener.java (2)
eternalcombat-plugin/src/main/java/com/eternalcode/combat/config/implementation/PluginConfig.java (1)
  • PluginConfig (14-136)
eternalcombat-plugin/src/main/java/com/eternalcode/combat/util/ReflectUtil.java (1)
  • ReflectUtil (6-23)
🔇 Additional comments (8)
eternalcombat-plugin/src/main/java/com/eternalcode/combat/CombatPlugin.java (1)

187-188: The new listeners look good!

The EndCrystalListener and RespawnAnchorListener are properly registered with the plugin instance, fight manager, and config.

eternalcombat-plugin/src/main/java/com/eternalcode/combat/util/ReflectUtil.java (1)

11-21: Document reflection usage warnings.

Using reflection to access non-public methods can lead to compatibility issues with future Bukkit versions.

This utility is used to call getDamagerBlockState which appears to be a non-public API method. Please add a comment explaining this approach and the potential risks:

 @SuppressWarnings("unchecked")
+/**
+ * Invokes a method on an object using reflection.
+ * Note: This bypasses access controls and may break with future API changes.
+ * Currently used to access getDamagerBlockState in EntityDamageByBlockEvent.
+ *
+ * @param object The object to invoke the method on
+ * @param name The name of the method to invoke
+ * @return The result of the method invocation
+ * @throws RuntimeException If reflection fails
+ */
 public static <T> T invokeMethod(Object object, String name) {
eternalcombat-plugin/src/main/java/com/eternalcode/combat/crystalpvp/CrystalPvpSettings.java (1)

1-14: Configuration class looks good

The settings are well organized with descriptive names and good default values.

eternalcombat-plugin/src/main/java/com/eternalcode/combat/crystalpvp/CrystalMetadata.java (2)

10-10: Class visibility is package-private

Class is package-private, which is good for encapsulation since it's only used within this package.


19-21: Good use of Optional for nullable value

Using Optional for the damager UUID is a good practice for handling possible null values safely.

eternalcombat-plugin/src/main/java/com/eternalcode/combat/crystalpvp/EndCrystalListener.java (2)

52-79: Good condition handling for tagging players

The code properly handles player vs. crystal damage events, checks metadata, and applies tagging to both players appropriately.


67-76: Tag both players with the same duration

Both players are tagged with the same combat timer duration, which is good for consistency.

eternalcombat-plugin/src/main/java/com/eternalcode/combat/crystalpvp/RespawnAnchorListener.java (1)

39-41: Good use of early return based on configuration

Checking the configuration at the start of the method and returning early is a good practice.

Took 4 minutes
@CitralFlo CitralFlo requested a review from imDMK May 7, 2025 20:28
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (1)
eternalcombat-plugin/src/main/java/com/eternalcode/combat/crystalpvp/CrystalPvpConstants.java (1)

40-44: Reflection safety reminder
Reflection may break on future Bukkit updates; wrap the call in try/catch or a version check to avoid hard errors.

🧹 Nitpick comments (5)
eternalcombat-plugin/src/main/java/com/eternalcode/combat/fight/controller/FightUnTagController.java (3)

35-43: Consider simplifying optionalKiller handling

You could build optionalKiller in one statement to reduce branches:

Optional<UUID> optionalKiller = killer != null
        ? Optional.of(killer.getUniqueId())
        : this.getCrystalKiller(player);

It reads a little cleaner and avoids the extra if/else.


48-54: Guard against attacker release when victim == attacker

When a player kills themselves (e.g., crystal suicide), optionalKiller may equal playerUniqueId, causing a redundant second untag call. A quick equality check avoids useless work.

-if (optionalKiller.isPresent() && this.config.combat.releaseAttackerOnVictimDeath) {
+if (optionalKiller.isPresent()
+        && !optionalKiller.get().equals(playerUniqueId)
+        && this.config.combat.releaseAttackerOnVictimDeath) {

74-84: Null‐safe early exit

getLastDamageCause() can return null. A fast null check keeps the method tidy:

 EntityDamageEvent lastDamageCause = player.getLastDamageCause();
-if (lastDamageCause instanceof EntityDamageByBlockEvent ...
+if (lastDamageCause == null) {
+    return Optional.empty();
+}
+if (lastDamageCause instanceof EntityDamageByBlockEvent ...
eternalcombat-plugin/src/main/java/com/eternalcode/combat/crystalpvp/RespawnAnchorListener.java (1)

90-97: Minor: streamline combat-tag call

You can inline the optional and skip an extra variable:

CrystalPvpConstants.handleCombatTag(
        CrystalPvpConstants.getDamagerUUIDFromRespawnAnchor(event),
        player, this.fightManager, this.pluginConfig);
eternalcombat-plugin/src/main/java/com/eternalcode/combat/crystalpvp/CrystalPvpConstants.java (1)

57-78: Tiny readability tweak

Fetching the value from the optional twice can be avoided:

optionalDamagerUUID
        .filter(id -> !id.equals(victimUniqueId))
        .ifPresent(id -> {
            int duration = pluginConfig.settings.combatTimerDuration;
            fightManager.tag(id, duration, CauseOfTag.CRYSTAL);
            fightManager.tag(victimUniqueId, duration, CauseOfTag.CRYSTAL);
        });

Same effect, a touch cleaner.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1287e67 and 7e52158.

📒 Files selected for processing (5)
  • eternalcombat-plugin/src/main/java/com/eternalcode/combat/crystalpvp/CrystalPvpConstants.java (1 hunks)
  • eternalcombat-plugin/src/main/java/com/eternalcode/combat/crystalpvp/EndCrystalListener.java (1 hunks)
  • eternalcombat-plugin/src/main/java/com/eternalcode/combat/crystalpvp/RespawnAnchorListener.java (1 hunks)
  • eternalcombat-plugin/src/main/java/com/eternalcode/combat/fight/controller/FightUnTagController.java (2 hunks)
  • eternalcombat-plugin/src/main/java/com/eternalcode/combat/util/ReflectUtil.java (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • eternalcombat-plugin/src/main/java/com/eternalcode/combat/util/ReflectUtil.java
  • eternalcombat-plugin/src/main/java/com/eternalcode/combat/crystalpvp/EndCrystalListener.java
🧰 Additional context used
🧬 Code Graph Analysis (1)
eternalcombat-plugin/src/main/java/com/eternalcode/combat/fight/controller/FightUnTagController.java (2)
eternalcombat-plugin/src/main/java/com/eternalcode/combat/crystalpvp/CrystalPvpConstants.java (1)
  • CrystalPvpConstants (18-81)
eternalcombat-plugin/src/main/java/com/eternalcode/combat/fight/logout/LogoutService.java (1)
  • LogoutService (11-37)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: build
🔇 Additional comments (1)
eternalcombat-plugin/src/main/java/com/eternalcode/combat/crystalpvp/RespawnAnchorListener.java (1)

36-43: Early config check looks good
Nice quick exit when the feature is disabled.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
eternalcombat-plugin/src/main/java/com/eternalcode/combat/crystalpvp/CrystalPvpConstants.java (1)

23-24: 🛠️ Refactor suggestion

Use unique metadata keys

Both constants are set to the same value, which risks collisions between crystal and anchor metadata. Re-using the earlier suggestion: give each a distinct suffix.

-public static final String CRYSTAL_METADATA = "eternalcombat:crystal-pvp";
-public static final String ANCHOR_METADATA  = "eternalcombat:crystal-pvp";
+public static final String CRYSTAL_METADATA = "eternalcombat:crystal-pvp:crystal";
+public static final String ANCHOR_METADATA  = "eternalcombat:crystal-pvp:anchor";
🧹 Nitpick comments (1)
eternalcombat-plugin/src/main/java/com/eternalcode/combat/crystalpvp/CrystalPvpConstants.java (1)

61-70: Cache the reflective‐lookup result once

hasDamagerBlockState() repeats reflection every call. Storing the outcome in a static final saves a tiny bit of overhead and keeps the method tidy.

-private static boolean hasDamagerBlockState() {
-    boolean hasMethod = false;
-    try {
-        hasMethod = EntityDamageByBlockEvent.class.getDeclaredMethod("getDamagerBlockState") != null;
-    }
-    catch (NoSuchMethodException e) {
-        // Method does not exist
-    }
-    return hasMethod;
-}
+private static final boolean HAS_DAMAGER_BLOCK_STATE;
+static {
+    boolean present;
+    try {
+        present = EntityDamageByBlockEvent.class.getDeclaredMethod("getDamagerBlockState") != null;
+    } catch (NoSuchMethodException ignored) {
+        present = false;
+    }
+    HAS_DAMAGER_BLOCK_STATE = present;
+}
+
+static boolean hasDamagerBlockState() {
+    return HAS_DAMAGER_BLOCK_STATE;
+}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7e52158 and d53e244.

📒 Files selected for processing (1)
  • eternalcombat-plugin/src/main/java/com/eternalcode/combat/crystalpvp/CrystalPvpConstants.java (1 hunks)
🔇 Additional comments (1)
eternalcombat-plugin/src/main/java/com/eternalcode/combat/crystalpvp/CrystalPvpConstants.java (1)

80-94: Consider tagging self-damage too

If a player hurts themselves with a crystal/anchor, they won’t be tagged because damager == victim. Is that intentional? Many anti-combat-log plugins still tag self-inflicted damage to prevent abuse.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (1)
eternalcombat-plugin/src/main/java/com/eternalcode/combat/crystalpvp/RespawnAnchorListener.java (1)

34-77: ⚠️ Potential issue

Missing configuration check in onAnchorInteract method

The method doesn't check if tagging from respawn anchors is enabled before processing, unlike the onAnchorExplosion method which does this check at line 81. Add the configuration check at the beginning to maintain consistency.

@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
void onAnchorInteract(PlayerInteractEvent event) {
+    if (!this.pluginConfig.crystalPvp.tagFromRespawnAnchor) {
+        return;
+    }
    
    Block block = event.getClickedBlock();
    // rest of the method...
}
🧹 Nitpick comments (3)
eternalcombat-plugin/src/main/java/com/eternalcode/combat/crystalpvp/RespawnAnchorListener.java (3)

41-44: Unnecessary null check for Material type

Material.getType() returns an enum which can't be null. This check isn't needed and can be removed.

Material type = block.getType();
-if (type == null) {
-    return;
-}

46-76: Simplify code structure with early returns

The current nested conditions make the code hard to follow. Restructure using early returns and clearer condition checks for better readability.

-if (type.equals(Material.RESPAWN_ANCHOR)) {
-    if (block.getBlockData() instanceof RespawnAnchor respawnAnchor) {
-        if (respawnAnchor.getCharges() > 0 && event.getAction().equals(Action.RIGHT_CLICK_BLOCK)) {
-            ItemStack item = event.getItem();
-            if (item == null) {
-                block.setMetadata(
-                    ANCHOR_METADATA,
-                    new CrystalMetadata(this.plugin, event.getPlayer().getUniqueId())
-                );
-                return;
-            }
-
-            if (item.getType() != Material.GLOWSTONE) {
-                block.setMetadata(
-                    ANCHOR_METADATA,
-                    new CrystalMetadata(this.plugin, event.getPlayer().getUniqueId())
-                );
-                return;
-            }
-        }
-
-        if (respawnAnchor.getCharges() == respawnAnchor.getMaximumCharges() && event
-            .getAction()
-            .equals(Action.RIGHT_CLICK_BLOCK)) {
-            block.setMetadata(
-                ANCHOR_METADATA,
-                new CrystalMetadata(this.plugin, event.getPlayer().getUniqueId())
-            );
-        }
-    }
-}
+if (type != Material.RESPAWN_ANCHOR) {
+    return;
+}
+
+if (!(block.getBlockData() instanceof RespawnAnchor anchor)) {
+    return;
+}
+
+if (!event.getAction().equals(Action.RIGHT_CLICK_BLOCK)) {
+    return;
+}
+
+boolean shouldTag = false;
+
+ItemStack item = event.getItem();
+int charges = anchor.getCharges();
+int maxCharges = anchor.getMaximumCharges();
+
+if (charges > 0 && (item == null || item.getType() != Material.GLOWSTONE)) {
+    shouldTag = true;
+}
+
+if (charges == maxCharges) {
+    shouldTag = true;
+}
+
+if (shouldTag) {
+    block.setMetadata(ANCHOR_METADATA, new CrystalMetadata(this.plugin, event.getPlayer().getUniqueId()));
+}

89-95: Extract respawn anchor damage handling to a private method

This code could be more readable if the damager retrieval and combat tag handling were extracted to a separate method.

-Optional<UUID> optionalDamagerUniqueId = CrystalPvpConstants.getDamagerUUIDFromRespawnAnchor(event);
-
-if (optionalDamagerUniqueId.isEmpty()) {
-    return;
-}
-
-CrystalPvpConstants.handleCombatTag(optionalDamagerUniqueId, player, this.fightManager, this.pluginConfig);
+handleRespawnAnchorDamage(event, player);

You would then add this method:

private void handleRespawnAnchorDamage(EntityDamageByBlockEvent event, Player victim) {
    Optional<UUID> optionalDamagerUniqueId = CrystalPvpConstants.getDamagerUUIDFromRespawnAnchor(event);
    
    if (optionalDamagerUniqueId.isEmpty()) {
        return;
    }
    
    CrystalPvpConstants.handleCombatTag(optionalDamagerUniqueId, victim, this.fightManager, this.pluginConfig);
}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d53e244 and 188ab9b.

📒 Files selected for processing (2)
  • eternalcombat-plugin/src/main/java/com/eternalcode/combat/crystalpvp/CrystalPvpConstants.java (1 hunks)
  • eternalcombat-plugin/src/main/java/com/eternalcode/combat/crystalpvp/RespawnAnchorListener.java (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • eternalcombat-plugin/src/main/java/com/eternalcode/combat/crystalpvp/CrystalPvpConstants.java

…talpvp/CrystalPvpConstants.java

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
package com.eternalcode.combat.crystalpvp;
import com.eternalcode.combat.config.implementation.PluginConfig;
import com.eternalcode.combat.fight.FightManager;
import java.util.Optional;
import java.util.UUID;
import org.bukkit.entity.Arrow;
import org.bukkit.entity.EnderCrystal;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.EntityDamageByEntityEvent;
import org.bukkit.event.entity.EntityExplodeEvent;
import org.bukkit.plugin.Plugin;
import static com.eternalcode.combat.crystalpvp.CrystalPvpConstants.CRYSTAL_METADATA;
public class EndCrystalListener implements Listener {
private final Plugin plugin;
private final FightManager fightManager;
private final PluginConfig pluginConfig;
public EndCrystalListener(Plugin plugin, FightManager fightManager, PluginConfig pluginConfig) {
this.plugin = plugin;
this.fightManager = fightManager;
this.pluginConfig = pluginConfig;
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
void onPlayerDamageCrystal(EntityDamageByEntityEvent event) {
if (!(event.getEntity() instanceof EnderCrystal enderCrystal)) {
return;
}
if (!(event.getDamager() instanceof Player player) &&
!(event.getDamager() instanceof Arrow arrow && arrow.getShooter() instanceof Player player)) {
return;
}
if (!player.hasPermission("eternalcombat.crystalpvp.crystal")) {
return;
}
enderCrystal.setMetadata(CRYSTAL_METADATA, new CrystalMetadata(this.plugin, player.getUniqueId()));
}
@EventHandler(priority = EventPriority.MONITOR)
void onCrystalExplode(EntityExplodeEvent event) {
if (event.getEntity() instanceof EnderCrystal crystal) {
crystal.removeMetadata(CRYSTAL_METADATA, this.plugin);
}
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
void onDamage(EntityDamageByEntityEvent event) {
if (event.isCancelled()) {
return;
}
if (!this.pluginConfig.crystalPvp.tagFromCrystals) {
return;
}
if (pluginConfig.settings.ignoredWorlds.contains(event.getEntity().getWorld().getName())) {
return;
}
Optional<UUID> optionalDamagerUUID = CrystalPvpConstants.getDamagerUUIDFromEndCrystal(event);
if (optionalDamagerUUID.isEmpty()) {
return;
}
if (event.getEntity() instanceof Player player) {
CrystalPvpConstants.handleCombatTag(
optionalDamagerUUID,
player,
this.fightManager,
this.pluginConfig,
event.getCause()
);
}
}
}

Comment on lines +34 to +77
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
void onAnchorInteract(PlayerInteractEvent event) {
Block block = event.getClickedBlock();
if (block == null) {
return;
}

Material type = block.getType();
if (type == null) {
return;
}

if (type.equals(Material.RESPAWN_ANCHOR)) {
if (block.getBlockData() instanceof RespawnAnchor respawnAnchor) {
if (respawnAnchor.getCharges() > 0 && event.getAction().equals(Action.RIGHT_CLICK_BLOCK)) {
ItemStack item = event.getItem();
if (item == null) {
block.setMetadata(
ANCHOR_METADATA,
new CrystalMetadata(this.plugin, event.getPlayer().getUniqueId())
);
return;
}

if (item.getType() != Material.GLOWSTONE) {
block.setMetadata(
ANCHOR_METADATA,
new CrystalMetadata(this.plugin, event.getPlayer().getUniqueId())
);
return;
}
}

if (respawnAnchor.getCharges() == respawnAnchor.getMaximumCharges() && event
.getAction()
.equals(Action.RIGHT_CLICK_BLOCK)) {
block.setMetadata(
ANCHOR_METADATA,
new CrystalMetadata(this.plugin, event.getPlayer().getUniqueId())
);
}
}
}
}
Copy link
Member

Choose a reason for hiding this comment

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

This is not reca-condition?

Suggested change
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
void onAnchorInteract(PlayerInteractEvent event) {
Block block = event.getClickedBlock();
if (block == null) {
return;
}
Material type = block.getType();
if (type == null) {
return;
}
if (type.equals(Material.RESPAWN_ANCHOR)) {
if (block.getBlockData() instanceof RespawnAnchor respawnAnchor) {
if (respawnAnchor.getCharges() > 0 && event.getAction().equals(Action.RIGHT_CLICK_BLOCK)) {
ItemStack item = event.getItem();
if (item == null) {
block.setMetadata(
ANCHOR_METADATA,
new CrystalMetadata(this.plugin, event.getPlayer().getUniqueId())
);
return;
}
if (item.getType() != Material.GLOWSTONE) {
block.setMetadata(
ANCHOR_METADATA,
new CrystalMetadata(this.plugin, event.getPlayer().getUniqueId())
);
return;
}
}
if (respawnAnchor.getCharges() == respawnAnchor.getMaximumCharges() && event
.getAction()
.equals(Action.RIGHT_CLICK_BLOCK)) {
block.setMetadata(
ANCHOR_METADATA,
new CrystalMetadata(this.plugin, event.getPlayer().getUniqueId())
);
}
}
}
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
void onAnchorInteract(PlayerInteractEvent event) {
Player player = event.getPlayer();
if (!player.hasPermission("eternalcombat.crystalpvp.anchor")) {
return;
}
Block block = event.getClickedBlock();
if (block == null) {
return;
}
Material type = block.getType();
if (type == null || !type.equals(Material.RESPAWN_ANCHOR)) {
return;
}
if (block.getBlockData() instanceof RespawnAnchor respawnAnchor) {
synchronized (block) {
if (respawnAnchor.getCharges() > 0 && event.getAction().equals(Action.RIGHT_CLICK_BLOCK)) {
ItemStack item = event.getItem();
if (item == null || item.getType() != Material.GLOWSTONE) {
block.setMetadata(
ANCHOR_METADATA,
new CrystalMetadata(this.plugin, player.getUniqueId())
);
return;
}
}
if (respawnAnchor.getCharges() == respawnAnchor.getMaximumCharges() &&
event.getAction().equals(Action.RIGHT_CLICK_BLOCK)) {
block.setMetadata(
ANCHOR_METADATA,
new CrystalMetadata(this.plugin, player.getUniqueId())
);
}
}
}
}

}
}
}

Copy link
Member

Choose a reason for hiding this comment

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

cleanup maybe?

    @EventHandler(priority = EventPriority.MONITOR)
    void onAnchorBreak(BlockBreakEvent event) {
        if (event.getBlock().getType() == Material.RESPAWN_ANCHOR) {
            event.getBlock().removeMetadata(ANCHOR_METADATA, this.plugin);
        }
    }

Comment on lines +79 to +96
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
void onAnchorExplosion(EntityDamageByBlockEvent event) {
if (!this.pluginConfig.crystalPvp.tagFromRespawnAnchor) {
return;
}

if (!(event.getEntity() instanceof Player player)) {
return;
}

Optional<UUID> optionalDamagerUniqueId = CrystalPvpConstants.getDamagerUUIDFromRespawnAnchor(event);

if (optionalDamagerUniqueId.isEmpty()) {
return;
}

CrystalPvpConstants.handleCombatTag(optionalDamagerUniqueId, player, this.fightManager, this.pluginConfig);
}
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
void onAnchorExplosion(EntityDamageByBlockEvent event) {
if (!this.pluginConfig.crystalPvp.tagFromRespawnAnchor) {
return;
}
if (!(event.getEntity() instanceof Player player)) {
return;
}
Optional<UUID> optionalDamagerUniqueId = CrystalPvpConstants.getDamagerUUIDFromRespawnAnchor(event);
if (optionalDamagerUniqueId.isEmpty()) {
return;
}
CrystalPvpConstants.handleCombatTag(optionalDamagerUniqueId, player, this.fightManager, this.pluginConfig);
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
void onAnchorExplosion(EntityDamageByBlockEvent event) {
if (!this.pluginConfig.crystalPvp.tagFromRespawnAnchor) {
return;
}
if (!(event.getEntity() instanceof Player player)) {
return;
}
if (pluginConfig.settings.ignoredWorlds.contains(player.getWorld().getName())) {
return;
}
Optional<UUID> optionalDamagerUniqueId = CrystalPvpConstants.getDamagerUUIDFromRespawnAnchor(event);
if (optionalDamagerUniqueId.isEmpty()) {
return;
}
CrystalPvpConstants.handleCombatTag(
optionalDamagerUniqueId,
player,
this.fightManager,
this.pluginConfig,
event.getCause()
);
}

Comment on lines +1 to +95
package com.eternalcode.combat.crystalpvp;

import com.eternalcode.combat.config.implementation.PluginConfig;
import com.eternalcode.combat.fight.FightManager;
import com.eternalcode.combat.fight.event.CauseOfTag;
import com.eternalcode.combat.util.ReflectUtil;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import org.bukkit.Material;
import org.bukkit.block.BlockState;
import org.bukkit.entity.EnderCrystal;
import org.bukkit.entity.Player;
import org.bukkit.event.entity.EntityDamageByBlockEvent;
import org.bukkit.event.entity.EntityDamageByEntityEvent;
import org.bukkit.metadata.MetadataValue;

public class CrystalPvpConstants {

private CrystalPvpConstants() {
}

public static final String CRYSTAL_METADATA = "eternalcombat:crystal";
public static final String ANCHOR_METADATA = "eternalcombat:anchor";

public static Optional<UUID> getDamagerUUIDFromEndCrystal(EntityDamageByEntityEvent event) {
if (event.getDamager() instanceof EnderCrystal enderCrystal) {
List<MetadataValue> metadataValues = enderCrystal.getMetadata(CRYSTAL_METADATA);
return metadataValues
.stream()
.filter(source -> source instanceof CrystalMetadata)
.map(meta -> (CrystalMetadata) meta)
.findFirst()
.flatMap(CrystalMetadata::getDamager);
}
return Optional.empty();
}

public static Optional<UUID> getDamagerUUIDFromRespawnAnchor(EntityDamageByBlockEvent event) {
if (!CrystalPvpConstants.hasDamagerBlockState()) {
return Optional.empty();
}

Object maybeState = ReflectUtil.invokeMethod(event, "getDamagerBlockState");
if (!(maybeState instanceof BlockState state)) {
return Optional.empty();
}
Material type = state.getType();
if (!type.equals(Material.RESPAWN_ANCHOR)) {
return Optional.empty();
}

return state.getMetadata(ANCHOR_METADATA).stream()
.filter(source -> source instanceof CrystalMetadata)
.map(meta -> (CrystalMetadata) meta)
.findFirst()
.flatMap(metadata -> metadata.getDamager());
}

static boolean hasDamagerBlockState() {
boolean hasMethod = false;
try {
hasMethod = EntityDamageByBlockEvent.class.getDeclaredMethod("getDamagerBlockState") != null;
}
catch (NoSuchMethodException e) {
// Method does not exist
}
return hasMethod;
}

static void handleCombatTag(
Optional<UUID> optionalDamagerUUID,
Player player,
FightManager fightManager,
PluginConfig pluginConfig
) {
UUID victimUniqueId = player.getUniqueId();

if (optionalDamagerUUID.isPresent()) {
UUID damagerUniqueId = optionalDamagerUUID.get();
if (!damagerUniqueId.equals(victimUniqueId)) {
fightManager.tag(
damagerUniqueId,
pluginConfig.settings.combatTimerDuration,
CauseOfTag.CRYSTAL
);
fightManager.tag(
victimUniqueId,
pluginConfig.settings.combatTimerDuration,
CauseOfTag.CRYSTAL
);
}
}
}
}
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
package com.eternalcode.combat.crystalpvp;
import com.eternalcode.combat.config.implementation.PluginConfig;
import com.eternalcode.combat.fight.FightManager;
import com.eternalcode.combat.fight.event.CauseOfTag;
import com.eternalcode.combat.util.ReflectUtil;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import org.bukkit.Material;
import org.bukkit.block.BlockState;
import org.bukkit.entity.EnderCrystal;
import org.bukkit.entity.Player;
import org.bukkit.event.entity.EntityDamageByBlockEvent;
import org.bukkit.event.entity.EntityDamageByEntityEvent;
import org.bukkit.metadata.MetadataValue;
public class CrystalPvpConstants {
private CrystalPvpConstants() {
}
public static final String CRYSTAL_METADATA = "eternalcombat:crystal";
public static final String ANCHOR_METADATA = "eternalcombat:anchor";
public static Optional<UUID> getDamagerUUIDFromEndCrystal(EntityDamageByEntityEvent event) {
if (event.getDamager() instanceof EnderCrystal enderCrystal) {
List<MetadataValue> metadataValues = enderCrystal.getMetadata(CRYSTAL_METADATA);
return metadataValues
.stream()
.filter(source -> source instanceof CrystalMetadata)
.map(meta -> (CrystalMetadata) meta)
.findFirst()
.flatMap(CrystalMetadata::getDamager);
}
return Optional.empty();
}
public static Optional<UUID> getDamagerUUIDFromRespawnAnchor(EntityDamageByBlockEvent event) {
if (!CrystalPvpConstants.hasDamagerBlockState()) {
return Optional.empty();
}
Object maybeState = ReflectUtil.invokeMethod(event, "getDamagerBlockState");
if (!(maybeState instanceof BlockState state)) {
return Optional.empty();
}
Material type = state.getType();
if (!type.equals(Material.RESPAWN_ANCHOR)) {
return Optional.empty();
}
return state.getMetadata(ANCHOR_METADATA).stream()
.filter(source -> source instanceof CrystalMetadata)
.map(meta -> (CrystalMetadata) meta)
.findFirst()
.flatMap(metadata -> metadata.getDamager());
}
static boolean hasDamagerBlockState() {
boolean hasMethod = false;
try {
hasMethod = EntityDamageByBlockEvent.class.getDeclaredMethod("getDamagerBlockState") != null;
}
catch (NoSuchMethodException e) {
// Method does not exist
}
return hasMethod;
}
static void handleCombatTag(
Optional<UUID> optionalDamagerUUID,
Player player,
FightManager fightManager,
PluginConfig pluginConfig
) {
UUID victimUniqueId = player.getUniqueId();
if (optionalDamagerUUID.isPresent()) {
UUID damagerUniqueId = optionalDamagerUUID.get();
if (!damagerUniqueId.equals(victimUniqueId)) {
fightManager.tag(
damagerUniqueId,
pluginConfig.settings.combatTimerDuration,
CauseOfTag.CRYSTAL
);
fightManager.tag(
victimUniqueId,
pluginConfig.settings.combatTimerDuration,
CauseOfTag.CRYSTAL
);
}
}
}
}
package com.eternalcode.combat.crystalpvp;
import com.eternalcode.combat.config.implementation.PluginConfig;
import com.eternalcode.combat.fight.FightManager;
import com.eternalcode.combat.fight.event.CauseOfTag;
import com.eternalcode.combat.util.ReflectUtil;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import org.bukkit.Material;
import org.bukkit.block.BlockState;
import org.bukkit.entity.EnderCrystal;
import org.bukkit.entity.Player;
import org.bukkit.event.entity.EntityDamageByBlockEvent;
import org.bukkit.event.entity.EntityDamageByEntityEvent;
import org.bukkit.event.entity.EntityDamageEvent;
import org.bukkit.metadata.MetadataValue;
public class CrystalPvpConstants {
private CrystalPvpConstants() {
}
public static final String CRYSTAL_METADATA = "eternalcombat:crystal";
public static final String ANCHOR_METADATA = "eternalcombat:anchor";
public static Optional<UUID> getDamagerUUIDFromEndCrystal(EntityDamageByEntityEvent event) {
if (event.getDamager() instanceof EnderCrystal enderCrystal) {
List<MetadataValue> metadataValues = enderCrystal.getMetadata(CRYSTAL_METADATA);
if (metadataValues == null || metadataValues.isEmpty()) {
return Optional.empty();
}
return metadataValues
.stream()
.filter(source -> source instanceof CrystalMetadata)
.map(meta -> (CrystalMetadata) meta)
.findFirst()
.flatMap(CrystalMetadata::getDamager);
}
return Optional.empty();
}
public static Optional<UUID> getDamagerUUIDFromRespawnAnchor(EntityDamageByBlockEvent event) {
if (!CrystalPvpConstants.hasDamagerBlockState()) {
return Optional.empty();
}
Object maybeState = ReflectUtil.invokeMethod(event, "getDamagerBlockState");
if (!(maybeState instanceof BlockState state)) {
return Optional.empty();
}
Material type = state.getType();
if (!type.equals(Material.RESPAWN_ANCHOR)) {
return Optional.empty();
}
List<MetadataValue> metadataValues = state.getMetadata(ANCHOR_METADATA);
if (metadataValues == null || metadataValues.isEmpty()) {
return Optional.empty();
}
return metadataValues.stream()
.filter(source -> source instanceof CrystalMetadata)
.map(meta -> (CrystalMetadata) meta)
.findFirst()
.flatMap(metadata -> metadata.getDamager());
}
static boolean hasDamagerBlockState() {
try {
Method method = EntityDamageByBlockEvent.class.getDeclaredMethod("getDamagerBlockState");
return method != null && method.getReturnType().equals(BlockState.class);
} catch (NoSuchMethodException e) {
return false;
}
}
static void handleCombatTag(
Optional<UUID> optionalDamagerUUID,
Player player,
FightManager fightManager,
PluginConfig pluginConfig,
EntityDamageEvent.DamageCause cause
) {
if (cause != EntityDamageEvent.DamageCause.BLOCK_EXPLOSION &&
cause != EntityDamageEvent.DamageCause.ENTITY_EXPLOSION) {
return;
}
UUID victimUniqueId = player.getUniqueId();
if (optionalDamagerUUID.isPresent()) {
UUID damagerUniqueId = optionalDamagerUUID.get();
if (!damagerUniqueId.equals(victimUniqueId)) {
fightManager.tag(
damagerUniqueId,
pluginConfig.settings.combatTimerDuration,
CauseOfTag.CRYSTAL
);
fightManager.tag(
victimUniqueId,
pluginConfig.settings.combatTimerDuration,
CauseOfTag.CRYSTAL
);
}
}
}
}

Comment on lines +74 to +77
@Comment({
" ",

})
Copy link
Member

Choose a reason for hiding this comment

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

hm? why empty line?

import org.bukkit.event.entity.EntityDamageByEntityEvent;
import org.bukkit.metadata.MetadataValue;

public class CrystalPvpConstants {
Copy link
Member

Choose a reason for hiding this comment

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

I don't know if this class is named right @Rollczi?

Copy link
Member

Choose a reason for hiding this comment

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

If Pvp has only the first letter uppercased, then the method below should be renamed to getDamagerUuidFromEndCrystal

Comment on lines +51 to +72
block.setMetadata(
ANCHOR_METADATA,
new CrystalMetadata(this.plugin, event.getPlayer().getUniqueId())
);
return;
}

if (item.getType() != Material.GLOWSTONE) {
block.setMetadata(
ANCHOR_METADATA,
new CrystalMetadata(this.plugin, event.getPlayer().getUniqueId())
);
return;
}
}

if (respawnAnchor.getCharges() == respawnAnchor.getMaximumCharges() && event
.getAction()
.equals(Action.RIGHT_CLICK_BLOCK)) {
block.setMetadata(
ANCHOR_METADATA,
new CrystalMetadata(this.plugin, event.getPlayer().getUniqueId())
Copy link
Member

Choose a reason for hiding this comment

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

As far as I see we can extract the block.setMetadata(...) at the beginning/end of the method, it's repetitive

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.

4 participants