Skip to content

Create team system#214

Open
PrinzPizza42 wants to merge 43 commits intomasterfrom
Create-team-system
Open

Create team system#214
PrinzPizza42 wants to merge 43 commits intomasterfrom
Create-team-system

Conversation

@PrinzPizza42
Copy link
Collaborator

@PrinzPizza42 PrinzPizza42 commented Apr 21, 2025

Implementation of a team system

...

Notes for testing your change

...

Summary by CodeRabbit

  • New Features

    • Introduced a comprehensive team system with commands /teams and /team, enabling team creation, management, member roles, protected location claiming, and team enderchests with inventory persistence.
    • Added paginated GUIs for team settings, member management, color selection, and chunk claim radius configuration.
    • Enhanced player and team data persistence with extended database schema and integration.
    • Improved scoreboard integration reflecting team membership, player ranks, and AFK status dynamically.
    • Added visual particle effects to highlight protected team claim areas.
    • Added team-related event listeners to manage inventory reloads, block interactions, and enderchest restrictions.
  • Bug Fixes

    • Prevented enchanted items from being placed in team enderchests.
    • Fixed translation formatting for clickable messages.
  • Documentation

    • Added detailed "Team System" section to README describing features and commands.
  • Chores

    • Added new translation keys for all team-related features and messages.
    • Updated configuration to support protected location radius.
    • Adjusted database schema and Docker PostgreSQL settings for team data.
    • Refined sound effects and UI feedback for team interactions.
    • Removed deprecated GitHub Actions workflows and jobs.

created Team Settings Inventory Listener;
createed Teams Main Menu GUI;
created Team Settings CMD
…fter a change;

implemented that a deleted team can not be opened
change usage of player to playerData or offlinePlayer where possible;
…team-system

# Conflicts:
#	src/main/java/de/j/stationofdoom/teams/enderchest/TeamEnderchestPreventEnchantedItemsInput.java
change TeamsMainMenuGUI usage to get teams and so on to new class HandleTeams
cancel claiming of location when the location is already claimed
send team members a message when a new location is claimed
also show the location when claiming it
@PrinzPizza42 PrinzPizza42 linked an issue Apr 21, 2025 that may be closed by this pull request
5 tasks
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Apr 21, 2025

Walkthrough

This update introduces a comprehensive team system to the plugin, enabling players to create, manage, and interact with teams. Key features include team creation, color and name customization, operator roles, a shared team ender chest, location protection with claim radius, and a rank-based permission system. The implementation adds new commands, GUIs, database tables, and event listeners for team management, chunk claiming, and inventory interactions. The configuration and translation systems are extended to support these features. The codebase now includes robust persistence for teams and inventories, enhanced player data handling, and dynamic UI updates for team-related actions.

Changes

File(s) / Group Change Summary
README.md Added a "Team System" section describing new team management features.
docker-compose.yml Changed PostgreSQL port to 5432; added environment variables for application name and schema.
src/main/java/de/j/deathMinigames/database/Database.java Database initialization now creates tables for teams and team enderchests.
src/main/java/de/j/deathMinigames/database/PlayerDataDatabase.java Extended player data schema for team UUID and operator status; updated queries and logic for team fields; added methods for player data retrieval by UUID.
src/main/java/de/j/deathMinigames/database/TeamEnderchestsDatabase.java New: Singleton class for managing team enderchest inventories in the database.
src/main/java/de/j/deathMinigames/database/TeamsDatabase.java New: Singleton class for persisting and managing team data, members, and operators in the database.
src/main/java/de/j/deathMinigames/database/DBTeamMember.java New: Class modeling a team member with membership and operator status.
src/main/java/de/j/deathMinigames/listeners/AnvilListener.java Added support for renaming teams and setting claim radius via anvil GUI; updated sound effects.
src/main/java/de/j/deathMinigames/listeners/InventoryListener.java Added case for claim radius setting in main menu GUI interaction.
src/main/java/de/j/deathMinigames/listeners/JoinListener.java Refactored player data retrieval to use a method instead of direct map access; added logging.
src/main/java/de/j/deathMinigames/main/Config.java Added support for "ProtectedLocationRadius" config; method to update config value.
src/main/java/de/j/deathMinigames/main/HandlePlayers.java Adjusted imports; added player data retrieval by UUID; ensured database sync on new player; added logging.
src/main/java/de/j/deathMinigames/main/PlayerData.java Enhanced with team UUID/operator fields, offline player/location handling, and improved online status checks.
src/main/java/de/j/deathMinigames/settings/AnvilUI.java Extended input meta logic for team rename and claim radius actions.
src/main/java/de/j/deathMinigames/settings/GUI.java Changed inventory field visibility; added constructors; new method for adding player heads; removed batch clickable item method.
src/main/java/de/j/deathMinigames/settings/MainMenu.java Added claim radius anvil UI; removed host/server name enums; added claim radius menu item.
src/main/java/de/j/stationofdoom/main/Main.java Registered new team commands and listeners; updated database sync on plugin disable.
src/main/java/de/j/stationofdoom/teams/HandleTeams.java New: Singleton for managing teams, retrieval, existence checks, and player-team associations.
src/main/java/de/j/stationofdoom/teams/Team.java New: Comprehensive team class with membership, operator roles, color, protected location, and inventory management.
src/main/java/de/j/stationofdoom/teams/TeamCMD.java New: Command for accessing a player's team menu if a member.
src/main/java/de/j/stationofdoom/teams/TeamInventoryReload.java New: Listener for refreshing team GUIs after opening.
src/main/java/de/j/stationofdoom/teams/TeamPlayerSettingsGUI.java New: GUI for managing individual team member settings (roles, kick).
src/main/java/de/j/stationofdoom/teams/TeamSettingsGUI.java New: Paginated GUI for team settings, member management, and team actions.
src/main/java/de/j/stationofdoom/teams/TeamSettingsInventoryListener.java New: Listener for handling clicks in team-related GUIs, enforcing permissions and actions.
src/main/java/de/j/stationofdoom/teams/TeamsCMD.java New: Command for opening the main teams menu or accessing team enderchest.
src/main/java/de/j/stationofdoom/teams/TeamsMainMenuGUI.java New: Paginated GUI for browsing and creating teams, displaying team info.
src/main/java/de/j/stationofdoom/teams/chunkClaimSystem/BlockBreakAndUseCancelListener.java New: Listener to prevent unauthorized block interactions in claimed areas.
src/main/java/de/j/stationofdoom/teams/chunkClaimSystem/ChunkClaimSystem.java New: Singleton for managing team chunk claims, protection checks, and particle visualization.
src/main/java/de/j/stationofdoom/teams/enderchest/EnderchestInvHolder.java New: Inventory holder for team enderchests, with unique ID.
src/main/java/de/j/stationofdoom/teams/enderchest/TeamEnderchestPreventEnchantedItemsInputListener.java New: Listener to prevent enchanted items from being placed in team enderchests.
src/main/java/de/j/stationofdoom/util/Tablist.java Refactored for dynamic scoreboard teams, player ranks, AFK handling, and auto-reload.
src/main/resources/translations.json Added translations for all new team system features and UI; fixed formatting in lose message.
.github/workflows/gradle.yml Removed the publishToRegistry job from CI workflow.
.github/workflows/repo.yml Deleted GitHub Actions workflow for release publishing.

Sequence Diagram(s)

sequenceDiagram
    participant Player
    participant TeamsMainMenuGUI
    participant TeamSettingsGUI
    participant HandleTeams
    participant TeamsDatabase
    participant TeamEnderchestsDatabase
    participant ChunkClaimSystem

    Player->>TeamsMainMenuGUI: /teams command
    TeamsMainMenuGUI->>TeamsDatabase: getTeams()
    TeamsDatabase-->>TeamsMainMenuGUI: List<Team>
    TeamsMainMenuGUI-->>Player: Show teams menu

    Player->>TeamSettingsGUI: Select team
    TeamSettingsGUI->>HandleTeams: getTeam(PlayerData)
    HandleTeams-->>TeamSettingsGUI: Team
    TeamSettingsGUI-->>Player: Show team settings

    Player->>TeamSettingsGUI: Rename team / change color / manage members
    TeamSettingsGUI->>TeamsDatabase: updateTeam/Player
    TeamsDatabase-->>TeamSettingsGUI: Ack

    Player->>TeamSettingsGUI: Access enderchest
    TeamSettingsGUI->>TeamEnderchestsDatabase: getTeamEnderchest(UUID)
    TeamEnderchestsDatabase-->>TeamSettingsGUI: Inventory
    TeamSettingsGUI-->>Player: Open enderchest

    Player->>ChunkClaimSystem: Claim location
    ChunkClaimSystem->>HandleTeams: getTeam(Player)
    HandleTeams-->>ChunkClaimSystem: Team
    ChunkClaimSystem->>TeamsDatabase: updateTeam(Team)
    TeamsDatabase-->>ChunkClaimSystem: Ack
    ChunkClaimSystem-->>Player: Show particles, send confirmation
Loading

Suggested reviewers

  • JonasFranke

Poem

🐰
A team is born with colors bright,
Enderchests gleam in shared delight.
Operators lead, while members play,
Protected lands keep griefers at bay.
Rename, promote, or claim your ground—
In menus and GUIs, new features abound!
With every hop, this code’s a cheer—
Teamwork and fun, the update is here!
🥕


📜 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 31210b9 and dc56c73.

📒 Files selected for processing (1)
  • .github/workflows/gradle.yml (0 hunks)
💤 Files with no reviewable changes (1)
  • .github/workflows/gradle.yml
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: build
  • GitHub Check: build

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.
    • Explain this complex logic.
    • 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 explain this code block.
    • @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 explain its main purpose.
    • @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.

Copy link
Contributor

@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: 38

🔭 Outside diff range comments (1)
src/main/java/de/j/stationofdoom/main/Main.java (1)

109-110: ⚠️ Potential issue

Duplicate event listener registration detected.

There is a duplicate registration of the PlayerSitListener, which should be removed.

 pluginManager.registerEvents(new PlayerSitListener(), this);
-pluginManager.registerEvents(new PlayerSitListener(), this);
♻️ Duplicate comments (1)
src/main/java/de/j/deathMinigames/database/TeamEnderchestsDatabase.java (1)

34-39: DDL again executed via insert()

Same remark as for TeamsDatabase#createTable: use .execute() (or
.update()) for DDL to avoid undefined behaviour.

🧹 Nitpick comments (32)
docker-compose.yml (2)

5-5: Caution: Standard PostgreSQL port might cause conflicts

You've changed the port mapping from a custom port to the standard PostgreSQL port 5432. While this is more conventional, be aware it could potentially conflict with other PostgreSQL instances running on the host machine.

Consider documenting this change and potential conflict in your installation instructions. Alternatively, you could keep using a non-standard port to avoid conflicts.


11-13: Add a newline at the end of the file

The file is missing a newline at the end, which is a standard practice for text files.

 - POSTGRES_DB=stationofdoom
 - POSTGRES_APPLICATION_NAME=stationofdoom
 - POSTGRES_SCHEMA=public
+
🧰 Tools
🪛 YAMLlint (1.35.1)

[error] 13-13: no new line character at the end of file

(new-line-at-end-of-file)

README.md (1)

89-100: Documentation is clear but missing a comma

The team system documentation is well-structured and provides a clear overview of the feature. However, there's a minor grammatical issue in line 92.

- Another feature of this plugin is its team system, which lets players create teams, to access it enter the command `/teams`. The teams are saved in the database if one is connected
+ Another feature of this plugin is its team system, which lets players create teams. To access it, enter the command `/teams`. The teams are saved in the database if one is connected.

Also consider adding a period at the end of the sentence.

🧰 Tools
🪛 LanguageTool

[uncategorized] ~92-~92: A comma might be missing here.
Context: ...ch lets players create teams, to access it enter the command /teams. The teams a...

(AI_EN_LECTOR_MISSING_PUNCTUATION_COMMA)

src/main/java/de/j/stationofdoom/teams/chunkClaimSystem/BlockBreakAndUseCancelListener.java (2)

13-28: Reduce redundant ChunkClaimSystem.getInstance() calls.

The listener logic is solid, but there are multiple redundant calls to get the singleton instance.

@EventHandler
public void onPlayerInteract(PlayerInteractEvent event) {
    Player player = event.getPlayer();
    if(player == null || event.getClickedBlock() == null) return;
    ChunkClaimSystem chunkClaimSystem = ChunkClaimSystem.getInstance();
    if(chunkClaimSystem.checkIfLocationProtectedFromPlayer(event.getClickedBlock().getX(), event.getClickedBlock().getZ(), player)) {
-        Team team = ChunkClaimSystem.getInstance().getTeam(event.getClickedBlock().getLocation());
+        Team team = chunkClaimSystem.getTeam(event.getClickedBlock().getLocation());
        if(team == null) {
            Main.getMainLogger().warning("Team is null when checking if location is protected");
        }
        player.sendMessage(Component.text(new TranslationFactory().getTranslation(player, "chunkClaimedByDifferentTeam", team.getName())).color(NamedTextColor.RED));
        event.setCancelled(true);
-        ChunkClaimSystem.getInstance().showPlayerProtectedLocationViaParticles(player, team.getProtectedLocation());
+        chunkClaimSystem.showPlayerProtectedLocationViaParticles(player, team.getProtectedLocation());
    }
}

24-24: Cache TranslationFactory instance for better performance.

Creating a new TranslationFactory for each message is inefficient, especially in frequently triggered event handlers.

-player.sendMessage(Component.text(new TranslationFactory().getTranslation(player, "chunkClaimedByDifferentTeam", team.getName())).color(NamedTextColor.RED));
+private static final TranslationFactory TRANSLATION_FACTORY = new TranslationFactory();
// Add the field at class level, then use:
+player.sendMessage(Component.text(TRANSLATION_FACTORY.getTranslation(player, "chunkClaimedByDifferentTeam", team.getName())).color(NamedTextColor.RED));
src/main/java/de/j/stationofdoom/teams/TeamsCMD.java (2)

11-12: Make TeamsMainMenuGUI field private.

Public fields can lead to unexpected modifications from other classes.

-public TeamsMainMenuGUI teamsMainMenuGUI = new TeamsMainMenuGUI();
+private final TeamsMainMenuGUI teamsMainMenuGUI = new TeamsMainMenuGUI();

18-29: Add help message for unknown arguments.

The command doesn't provide guidance when users enter unknown arguments.

@Override
public void execute(CommandSourceStack stack, String[] args) {
    Player player = (Player) stack.getSender();
    if(args.length == 0) {
        teamsMainMenuGUI.showPage(1, player);
    }
    else if(args.length == 1 && (args[0].equalsIgnoreCase("e") || args[0].equalsIgnoreCase("enderchest"))) {
        Team team = HandleTeams.getTeam(HandlePlayers.getInstance().getPlayerData(player.getUniqueId()));
        if(team == null || !team.isMember(player.getUniqueId())) return;
        team.accessEnderChest(player);
    }
+    else {
+        player.sendMessage(Component.text(new TranslationFactory().getTranslation(player, "teamCommandUsage")).color(NamedTextColor.YELLOW));
+    }
}
src/main/java/de/j/stationofdoom/teams/enderchest/TeamEnderchestPreventEnchantedItemsInputListener.java (2)

19-20: Add safe cast check.

The direct casting of event.getView().getPlayer() to Player could be unsafe in some contexts.

-Player player = (Player) event.getView().getPlayer();
-if(player == null) return;
+if(!(event.getView().getPlayer() instanceof Player player)) return;

21-21: Cache TranslationFactory for better performance.

Similar to the previous listener, creating a new TranslationFactory instance for each message is inefficient.

+private static final TranslationFactory TRANSLATION_FACTORY = new TranslationFactory();
// Add the field at class level, then use:
-player.sendMessage(Component.text(new TranslationFactory().getTranslation(player, "teamEnderchestPreventEnchantedItemsInput")).color(NamedTextColor.RED));
+player.sendMessage(Component.text(TRANSLATION_FACTORY.getTranslation(player, "teamEnderchestPreventEnchantedItemsInput")).color(NamedTextColor.RED));
src/main/java/de/j/deathMinigames/main/HandlePlayers.java (1)

149-159: Add JavaDoc documentation for the new method.

This method is important for the team system functionality but lacks documentation explaining its purpose and behavior.

+/**
+ * Retrieves player data either from the database (if connected) or from the local cache.
+ * If the player is not found locally and is online, adds them to the known players.
+ *
+ * @param uuidOfPlayer The UUID of the player whose data to retrieve
+ * @return The player's data, or null if the player cannot be found and is offline
+ */
 public PlayerData getPlayerData(UUID uuidOfPlayer) {
src/main/java/de/j/deathMinigames/settings/AnvilUI.java (1)

78-85: Consider simplifying the team name retrieval logic.

The current implementation contains nested conditionals and could be simplified for better readability.

-                case TEAM_RENAME:
-                    if(HandleTeams.getTeam(HandlePlayers.getInstance().getPlayerData(player.getUniqueId())).getName() != null) {
-                        inputItemName = HandleTeams.getTeam(HandlePlayers.getInstance().getPlayerData(player.getUniqueId())).getName();
-                    }
-                    else {
-                        inputItemName = "";
-                    }
-                    break;
+                case TEAM_RENAME:
+                    Team playerTeam = HandleTeams.getTeam(HandlePlayers.getInstance().getPlayerData(player.getUniqueId()));
+                    inputItemName = playerTeam.getName() != null ? playerTeam.getName() : "";
+                    break;
src/main/java/de/j/deathMinigames/settings/MainMenu.java (1)

88-90: Consider adding synchronized keyword for consistency.

The getSetClaimingRadius() method should be synchronized for consistency with other getter methods in the class.

-    public static AnvilUI getSetClaimingRadius() {
+    public synchronized static AnvilUI getSetClaimingRadius() {
         return setClaimingRadius;
     }
src/main/java/de/j/stationofdoom/teams/TeamCMD.java (1)

14-16: Unused field detected.

The teamsMainMenuGUI field is instantiated but never used in this class.

 public class TeamCMD implements BasicCommand {
-    public TeamsMainMenuGUI teamsMainMenuGUI = new TeamsMainMenuGUI();
src/main/java/de/j/stationofdoom/teams/TeamsMainMenuGUI.java (1)

59-68: Optimize team inventory filling method

The early return logic in addTeamsToInventory is a bit complex with multiple conditions. Consider simplifying this for better readability.

 private void addTeamsToInventory(int intToStartFrom) {
     invSlots.clear();
     for(int teamsAdded = 0; teamsAdded < maxSlotsPerPage; teamsAdded++) {
-        if(teamsAdded >= teamQuantity || teamsAdded >= maxSlotsPerPage || intToStartFrom + teamsAdded >= teamQuantity) return;
+        // Check if we've reached the end of available teams
+        int teamIndex = intToStartFrom + teamsAdded;
+        if(teamIndex >= teamQuantity) return;
+        
-        Team currentTeam = TeamsMainMenuGUI.teams.get(intToStartFrom + teamsAdded);
+        Team currentTeam = TeamsMainMenuGUI.teams.get(teamIndex);
         ArrayList<String> lore = getTeamInformation(currentTeam);
         addClickableItemStack(currentTeam.getName(), currentTeam.getColorAsMaterial(), 1, teamsAdded, lore);
         invSlots.put(teamsAdded, currentTeam);
     }
 }
src/main/java/de/j/stationofdoom/teams/TeamInventoryReload.java (1)

13-13: Make reload interval configurable

Consider making the reload interval configurable through a config file rather than hardcoding it. This would allow server administrators to adjust it based on server performance needs.

-    private static final int secondsBetweenReloads = 3;
+    private static final int secondsBetweenReloads = Main.getPlugin().getConfig().getInt("teams.inventoryReloadInterval", 3);
src/main/java/de/j/stationofdoom/teams/TeamSettingsGUI.java (2)

36-66: Handle team deletion gracefully

The handling of deleted teams is good - showing an error message and redirecting to the main menu. However, consider adding logging when this happens to help with debugging.

 if(this.team.isDeleted()) {
     playerToShowTheInvTo.sendMessage(Component.text(tf.getTranslation(playerToShowTheInvTo, "teamDeleted", team.getName())).color(NamedTextColor.RED));
+    Main.getMainLogger().warning("Player " + playerToShowTheInvTo.getName() + " attempted to view deleted team: " + team.getName());
     new TeamsMainMenuGUI().showPage(1, playerToShowTheInvTo);
     return;
 }

116-139: Use constants for inventory slot positions

The inventory slot positions are hardcoded throughout the method. Consider using constants for these positions to make the code more maintainable.

 public class TeamSettingsGUI extends GUI {
+    // Slot constants for team function buttons
+    private static final int SLOT_RENAME = 9;
+    private static final int SLOT_COLOR = 10;
+    private static final int SLOT_LOCK = 11;
+    private static final int SLOT_ENDERCHEST = 12;
+    private static final int SLOT_CLAIM = 13;
+    private static final int SLOT_DELETE = 17;
     
     // Existing code...
     
     private void addFunctionsToInventory(Player player) {
-        addClickableItemStack(tf.getTranslation(player, "renameTeam"), Material.NAME_TAG, 1, 9);
-        addClickableItemStack(tf.getTranslation(player, "changeColor"), Material.ORANGE_GLAZED_TERRACOTTA, 1, 10);
+        addClickableItemStack(tf.getTranslation(player, "renameTeam"), Material.NAME_TAG, 1, SLOT_RENAME);
+        addClickableItemStack(tf.getTranslation(player, "changeColor"), Material.ORANGE_GLAZED_TERRACOTTA, 1, SLOT_COLOR);
         // And so on for other slots...
     }
src/main/java/de/j/stationofdoom/teams/HandleTeams.java (1)

84-86: Expose an immutable view of internal state

getAllTeams() hands out the live list. External code can add / remove elements bypassing the validation in addTeam, breaking invariants.

-    public List<Team> getAllTeams() {
-        return teams;
+    public List<Team> getAllTeams() {
+        return Collections.unmodifiableList(teams);
     }
src/main/java/de/j/deathMinigames/database/PlayerDataDatabase.java (1)

50-53: Column name casing is inconsistent

Table definition uses UUID (uppercase) but queries select/insert using uuid (lowercase). MySQL is case‑insensitive, but PostgreSQL (and some JDBC drivers) are not. Standardise on one casing to keep cross‑DB support.

Also applies to: 71-72, 145-146

src/main/java/de/j/stationofdoom/teams/TeamPlayerSettingsGUI.java (1)

13-16: Minor: mark helpers final & remove unnecessary volatile

TranslationFactory, playerData, and team are always accessed on the Bukkit main thread. volatile adds overhead without benefit here; final communicates immutability.

-private TranslationFactory tf = new TranslationFactory();
-private volatile PlayerData playerData;
-private volatile Team team;
+private final TranslationFactory tf = new TranslationFactory();
+private PlayerData playerData;
+private Team team;
src/main/java/de/j/deathMinigames/database/TeamsDatabase.java (1)

86-115: Duplicate team inserts & missing UNIQUE constraint

insertTeam will insert a new row every time the plugin shuts down even if the
team already existed. Over time the table will accumulate duplicates.

  1. Add PRIMARY KEY (uuid) or a UNIQUE constraint to the table.
  2. Use INSERT … ON CONFLICT (uuid) DO UPDATE or library equivalent.
src/main/java/de/j/stationofdoom/teams/chunkClaimSystem/ChunkClaimSystem.java (2)

174-195: Division by zero & floating‑point precision in vector normalisation

changeVector = vector.clone().multiply(1 / vector.length());

  • If both locations are identical vector.length() is 0 → division by zero.
  • Re‑calculating vector.length() every tick inside the runnable is
    unnecessary.
double length = vector.length();
if (length == 0) return;     // nothing to draw
Vector changeVector = vector.clone().multiply(1.0 / length);

49-64: checkIfLocationProtectedFromPlayer rebuilds map on every call

For every protection check you rebuild HashMap<Location,Team> resulting in
O(n) complexity per block interaction.

Cache the map once per tick or maintain it incrementally when teams change.

src/main/java/de/j/deathMinigames/main/PlayerData.java (1)

31-37: Stale Player instance

getPlayer() caches the Player reference forever; after a disconnect the object becomes invalid and many Paper calls will throw.
Consider resolving fresh with Bukkit.getPlayer(uuid) each call or at least when isOnline() returns false.

src/main/java/de/j/stationofdoom/teams/TeamSettingsInventoryListener.java (2)

214-233: Color‑changer GUI fills duplicates every click

addColorsToColorChanger appends all concrete blocks each time the color changer is opened, creating duplicate stacks and wasting memory.

Guard with if (colorChanger.firstEmpty() == -1) return; or populate once during GUI construction.


110-132: Complex permission condition is hard to read

The long if(slot >= 9 && slot <=13 ... ) mixes precedence of && and ||. Extract booleans or add parentheses to avoid subtle bugs.

boolean isEditableSlot = (slot >= 9 && slot <= 13) || slot == 17 ||
                         (slot >= 18 && slot <= 44 && inv.getItem(slot) != null);
if(isEditableSlot){
    ...
}
src/main/java/de/j/stationofdoom/util/Tablist.java (1)

42-62: Consider refactoring the scoreboard creation logic

The setScoreboard() method recreates all teams on each call, which could be inefficient. Consider implementing a strategy to update only what's needed.

Consider implementing a more selective update approach that only recreates teams when team data changes rather than on every call. You could track when teams are added or modified and only update the affected scoreboard teams.

src/main/java/de/j/stationofdoom/teams/Team.java (5)

253-255: Simplify redundant condition check in isTeamOperator

The method checks if the player is a member twice.

public boolean isTeamOperator(PlayerData playerData) {
    if(!this.members.containsKey(playerData.getUniqueId())) return false;
-   return this.members.containsKey(playerData.getUniqueId()) && this.members.get(playerData.getUniqueId());
+   return this.members.get(playerData.getUniqueId());
}

262-273: Unnecessary OfflinePlayer retrieval in isMember

The method retrieves an OfflinePlayer object but never uses it, which is inefficient.

public boolean isMember(UUID uuidOfPlayerTOCheck) {
    if (uuidOfPlayerTOCheck == null) {
        return false;
    }
-   OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(uuidOfPlayerTOCheck);
-   for (UUID uuid : this.members.keySet()) {
-       if(uuid.equals(uuidOfPlayerTOCheck)) {
-           return true;
-       }
-   }
-   return false;
+   return this.members.containsKey(uuidOfPlayerTOCheck);
}

140-146: Improve error handling in setLocked method

The method simply logs a message and returns if there are no operators, but it would be more helpful to throw an exception or return a status.

public void setLocked(boolean locked) {
    if(getTeamOperators().isEmpty()) {
-       Main.getMainLogger().info("Team is not locked because there are no operators");
-       return;
+       Main.getMainLogger().warning("Cannot " + (locked ? "lock" : "unlock") + " team " + name + " because there are no operators");
+       throw new IllegalStateException("Cannot change lock state for team without operators");
    }
    this.locked = locked;
}

171-179: Improve efficiency in setRandomConcreteMaterial

The method creates a new list and scans all Material values on each call, which is inefficient.

+private static final List<Material> CONCRETE_MATERIALS = new ArrayList<>();
+
+static {
+    for (Material material : Material.values()) {
+        if (material.name().contains("CONCRETE") && !material.name().contains("POWDER")) {
+            CONCRETE_MATERIALS.add(material);
+        }
+    }
+}

private void setRandomConcreteMaterial() {
-   List<Material> materials = new ArrayList<>();
-   for (Material material : Material.values()) {
-       if (material.name().contains("CONCRETE") && !material.name().contains("POWDER")) {
-           materials.add(material);
-       }
-   }
-   this.colorAsMaterial = materials.get(ThreadLocalRandom.current().nextInt(0, materials.size()));
+   this.colorAsMaterial = CONCRETE_MATERIALS.get(ThreadLocalRandom.current().nextInt(0, CONCRETE_MATERIALS.size()));
+   convertMaterialToString(this.colorAsMaterial);
}

29-33: Consider thread safety for shared team data

The class uses volatile for the inventory field, but other shared data like members and protected location aren't thread-safe.

Consider using more comprehensive thread safety mechanisms for all shared data in the Team class. You're already using volatile for the inventory field, but you should extend thread safety to other fields like members and protectedLocation that might be accessed concurrently.

Options include:

  1. Using synchronized blocks around methods that modify shared state
  2. Using concurrent collections like ConcurrentHashMap for members
  3. Using AtomicReference for fields like protectedLocation

This is especially important if team operations can happen across multiple threads.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between bce35bd and 09d7308.

📒 Files selected for processing (31)
  • README.md (1 hunks)
  • docker-compose.yml (1 hunks)
  • src/main/java/de/j/deathMinigames/database/Database.java (1 hunks)
  • src/main/java/de/j/deathMinigames/database/PlayerDataDatabase.java (6 hunks)
  • src/main/java/de/j/deathMinigames/database/TeamEnderchestsDatabase.java (1 hunks)
  • src/main/java/de/j/deathMinigames/database/TeamsDatabase.java (1 hunks)
  • src/main/java/de/j/deathMinigames/listeners/AnvilListener.java (5 hunks)
  • src/main/java/de/j/deathMinigames/listeners/InventoryListener.java (1 hunks)
  • src/main/java/de/j/deathMinigames/listeners/JoinListener.java (1 hunks)
  • src/main/java/de/j/deathMinigames/main/Config.java (3 hunks)
  • src/main/java/de/j/deathMinigames/main/HandlePlayers.java (4 hunks)
  • src/main/java/de/j/deathMinigames/main/PlayerData.java (7 hunks)
  • src/main/java/de/j/deathMinigames/settings/AnvilUI.java (2 hunks)
  • src/main/java/de/j/deathMinigames/settings/GUI.java (4 hunks)
  • src/main/java/de/j/deathMinigames/settings/MainMenu.java (4 hunks)
  • src/main/java/de/j/stationofdoom/main/Main.java (5 hunks)
  • src/main/java/de/j/stationofdoom/teams/HandleTeams.java (1 hunks)
  • src/main/java/de/j/stationofdoom/teams/Team.java (1 hunks)
  • src/main/java/de/j/stationofdoom/teams/TeamCMD.java (1 hunks)
  • src/main/java/de/j/stationofdoom/teams/TeamInventoryReload.java (1 hunks)
  • src/main/java/de/j/stationofdoom/teams/TeamPlayerSettingsGUI.java (1 hunks)
  • src/main/java/de/j/stationofdoom/teams/TeamSettingsGUI.java (1 hunks)
  • src/main/java/de/j/stationofdoom/teams/TeamSettingsInventoryListener.java (1 hunks)
  • src/main/java/de/j/stationofdoom/teams/TeamsCMD.java (1 hunks)
  • src/main/java/de/j/stationofdoom/teams/TeamsMainMenuGUI.java (1 hunks)
  • src/main/java/de/j/stationofdoom/teams/chunkClaimSystem/BlockBreakAndUseCancelListener.java (1 hunks)
  • src/main/java/de/j/stationofdoom/teams/chunkClaimSystem/ChunkClaimSystem.java (1 hunks)
  • src/main/java/de/j/stationofdoom/teams/enderchest/EnderchestInvHolder.java (1 hunks)
  • src/main/java/de/j/stationofdoom/teams/enderchest/TeamEnderchestPreventEnchantedItemsInputListener.java (1 hunks)
  • src/main/java/de/j/stationofdoom/util/Tablist.java (2 hunks)
  • src/main/resources/translations.json (4 hunks)
🧰 Additional context used
🧠 Learnings (3)
src/main/java/de/j/deathMinigames/main/HandlePlayers.java (2)
Learnt from: PrinzPizza42
PR: AtomPilz-DevTeam/StationofDoomPlugin#176
File: src/main/java/de/j/deathMinigames/database/PlayerDataDatabase.java:88-110
Timestamp: 2024-12-09T16:43:34.543Z
Learning: In `src/main/java/de/j/deathMinigames/database/PlayerDataDatabase.java`, exceptions from database operations (e.g., in methods like `updatePlayerDataDatabase`) are already handled by the database utility tool in use, so explicit exception handling in these methods is unnecessary.
Learnt from: PrinzPizza42
PR: AtomPilz-DevTeam/StationofDoomPlugin#176
File: src/main/java/de/j/deathMinigames/deathMinigames/Main.java:65-71
Timestamp: 2024-11-04T20:14:53.574Z
Learning: In the `Main.java` file of the Minecraft plugin, to prevent multithreading issues, objects such as `JumpAndRun`, `Minigame`, `Introduction`, `Config`, and `TranslationFactory` are instantiated within methods like `minigameStart` instead of being class-level fields.
src/main/java/de/j/stationofdoom/main/Main.java (1)
Learnt from: PrinzPizza42
PR: AtomPilz-DevTeam/StationofDoomPlugin#176
File: src/main/java/de/j/deathMinigames/dmUtil/TestCMD.java:23-27
Timestamp: 2024-12-05T07:34:55.227Z
Learning: In `src/main/java/de/j/deathMinigames/dmUtil/TestCMD.java`, the `TestCMD` command is intended to provide players with database connection information, including connection details, to help users who cannot see the code check if and where the database is connected.
src/main/java/de/j/deathMinigames/settings/GUI.java (1)
Learnt from: PrinzPizza42
PR: AtomPilz-DevTeam/StationofDoomPlugin#176
File: src/main/java/de/j/deathMinigames/settings/GUI.java:26-26
Timestamp: 2024-11-10T19:55:42.061Z
Learning: In the `GUI` class in `src/main/java/de/j/deathMinigames/settings/GUI.java`, the GUI instances are static and the `InventoryListener` is not, so using the same instance for every GUI is not possible.
🧬 Code Graph Analysis (7)
src/main/java/de/j/deathMinigames/listeners/InventoryListener.java (1)
src/main/java/de/j/deathMinigames/settings/MainMenu.java (1)
  • MainMenu (16-328)
src/main/java/de/j/deathMinigames/listeners/JoinListener.java (1)
src/main/java/de/j/deathMinigames/main/HandlePlayers.java (1)
  • HandlePlayers (12-160)
src/main/java/de/j/stationofdoom/teams/TeamsCMD.java (2)
src/main/java/de/j/deathMinigames/main/HandlePlayers.java (1)
  • HandlePlayers (12-160)
src/main/java/de/j/stationofdoom/teams/HandleTeams.java (1)
  • HandleTeams (11-87)
src/main/java/de/j/deathMinigames/settings/AnvilUI.java (4)
src/main/java/de/j/deathMinigames/main/HandlePlayers.java (1)
  • HandlePlayers (12-160)
src/main/java/de/j/stationofdoom/teams/HandleTeams.java (1)
  • HandleTeams (11-87)
src/main/java/de/j/stationofdoom/teams/chunkClaimSystem/ChunkClaimSystem.java (1)
  • ChunkClaimSystem (19-196)
src/main/java/de/j/stationofdoom/util/Tablist.java (1)
  • Tablist (20-356)
src/main/java/de/j/deathMinigames/main/HandlePlayers.java (3)
src/main/java/de/j/deathMinigames/database/Database.java (1)
  • Database (14-143)
src/main/java/de/j/deathMinigames/database/PlayerDataDatabase.java (1)
  • PlayerDataDatabase (16-190)
src/main/java/de/j/stationofdoom/main/Main.java (1)
  • Main (44-172)
src/main/java/de/j/deathMinigames/settings/GUI.java (1)
src/main/java/de/j/deathMinigames/main/HandlePlayers.java (1)
  • HandlePlayers (12-160)
src/main/java/de/j/deathMinigames/database/TeamsDatabase.java (3)
src/main/java/de/j/stationofdoom/main/Main.java (1)
  • Main (44-172)
src/main/java/de/j/stationofdoom/teams/HandleTeams.java (1)
  • HandleTeams (11-87)
src/main/java/de/j/stationofdoom/teams/Team.java (1)
  • Team (24-293)
🪛 YAMLlint (1.35.1)
docker-compose.yml

[error] 13-13: no new line character at the end of file

(new-line-at-end-of-file)

🪛 LanguageTool
README.md

[uncategorized] ~92-~92: A comma might be missing here.
Context: ...ch lets players create teams, to access it enter the command /teams. The teams a...

(AI_EN_LECTOR_MISSING_PUNCTUATION_COMMA)

🔇 Additional comments (36)
src/main/java/de/j/deathMinigames/listeners/InventoryListener.java (1)

259-261: LGTM: Menu integration for claiming radius setting

The implementation correctly integrates with the MainMenu's claiming radius configuration UI, which is part of the new team system.

This change sensibly extends the main menu with team-related functionality while maintaining the existing pattern of menu navigation.

src/main/java/de/j/deathMinigames/listeners/JoinListener.java (1)

40-40: Improved player data retrieval with abstraction

The change appropriately uses the singleton instance method to get player data, which properly abstracts database access and aligns with the centralized data management approach.

This change enhances maintainability by using the proper accessor that handles both in-memory and database-backed player data retrieval.

src/main/java/de/j/deathMinigames/database/Database.java (1)

80-81: Team database tables integration looks good.

The addition of team-related database table initialization is properly integrated into the existing database setup flow.

src/main/java/de/j/deathMinigames/main/Config.java (3)

3-3: New import for ChunkClaimSystem is appropriate.

This import is used to reference the ChunkClaimSystem singleton instance in the updated configuration handling.


127-129: Correctly integrates ChunkClaimSystem with config values.

The implementation properly checks if the configuration contains the key before attempting to read it, following the pattern used for other configuration values.


233-238: Well-implemented synchronized configuration method.

The method includes proper early-return optimization for unchanged values, maintains thread safety with synchronization, and follows the class's established pattern for config modification methods.

src/main/resources/translations.json (3)

68-68: Fixed formatting in German loseMessage.

The message formatting has been corrected, ensuring consistent presentation of the death location and difficulty reduction instructions.


188-188: Fixed formatting in English loseMessage.

Similar to the German version, the English message formatting has been properly corrected.


87-239: Comprehensive translation keys for the new team system.

The translations are well-structured and complete for both German and English locales, covering all aspects of the team system including:

  • Team creation and management
  • Member roles and permissions
  • Team enderchest functionality
  • Chunk claiming and protection
  • Error messages and notifications

This provides good internationalization support for the new features.

src/main/java/de/j/deathMinigames/main/HandlePlayers.java (3)

3-7: Updated imports to support database integration.

The imports have been appropriately updated to include Database and Bukkit classes needed for the enhanced player data management.


61-61: Method name standardization for UUID access.

Changed from getUUID() to getUniqueId(), which is the standard method name in Bukkit's API, improving consistency.


91-91: Immediate database persistence for new players.

Adding players to the database immediately after adding them to the local map ensures data consistency between memory and persistent storage.

src/main/java/de/j/deathMinigames/settings/AnvilUI.java (2)

3-5: Appropriate imports added for team system integration.

The imports correctly include the necessary dependencies for team functionality and chunk claim system integration.


86-88: Good implementation of claim radius input.

The code correctly retrieves the protected location size from the ChunkClaimSystem singleton and converts it to a string.

src/main/java/de/j/deathMinigames/settings/MainMenu.java (3)

35-36: Appropriate enum values added for team system UI integration.

The new TEAM_RENAME and SET_CLAIMING_RADIUS enum values are well-integrated with the existing AnvilUIs enum.


103-103: Appropriate initialization of the claim radius setting UI.

The setClaimingRadius instance is properly initialized with the SET_CLAIMING_RADIUS enum value.


137-137: Good integration of claim radius setting in the main menu.

The added clickable item for SetClaimingRadius with Material.MAP is well-placed in the menu at slot 6.

src/main/java/de/j/stationofdoom/main/Main.java (5)

5-10: Appropriate imports for team system components.

The imports correctly include team command classes, listeners, and database access classes needed for the team system.


20-22: Good imports for team-related event handlers.

The additional imports for team inventory management and team commands are appropriate.


98-99: Well-documented team command registration.

The registration of the 'teams' and 'team' commands with descriptive help text is well-implemented.


125-128: Proper event listener registration for team system.

All necessary team-related listeners are registered with the plugin manager.


143-144: Good database persistence for team data.

The addition of team database updates during plugin disable ensures team data persistence.

src/main/java/de/j/stationofdoom/teams/TeamCMD.java (3)

1-13: Good package structure and imports for team command.

The package structure and imports are appropriate for the team command functionality.


17-20: Good permission check for command.

The canUse method correctly restricts the command to player entities only.


22-31: Well-implemented command execution with team validation.

The execute method properly validates if the player is in a team before showing the team settings GUI. The error message is appropriately localized.

src/main/java/de/j/deathMinigames/settings/GUI.java (7)

24-25: Improved field visibility for subclass access

The changes from private to protected for the inventory field allow subclasses to directly access and manipulate the inventory, which is essential for the team GUI implementations. This is a good architectural change that supports extensibility.


27-30: Default constructor implementation looks good

This new no-arg constructor creates a standard 54-slot inventory, which is a common size in Minecraft (6 rows). This will simplify subclass implementations that don't need custom sizing.


49-63: Flexible inventory sizing constructor added

This constructor enhances flexibility by allowing subclasses to specify inventory size. It duplicates some code from the original constructor but maintains the same functionality with size customization.


74-74: Improved null checking for player data

The updated null check prevents potential NullPointerExceptions by verifying both the PlayerData object and its UUID before proceeding.


77-77: Updated player head profile setting method

The code now uses setOwnerProfile with Bukkit.createProfile instead of the previous implementation. This is aligned with the renamed method in PlayerData (getUniqueId() instead of getUUID()).


85-94: Useful addition of single player head with lore method

This new method is a valuable addition that allows adding a single player head with custom lore to a specific slot. This will be particularly useful for the team GUIs that need to display team members with their status information.


148-148: Added explicit 'this' reference for clarity

Using this.inventory instead of just inventory improves code clarity by explicitly referencing the class field.

src/main/java/de/j/stationofdoom/teams/TeamInventoryReload.java (1)

15-23: Good event handling for inventory opening

The event handler correctly identifies team-related GUIs and initiates the reload timer. The class casting approach is efficient.

src/main/java/de/j/deathMinigames/database/PlayerDataDatabase.java (1)

81-96:

Details

❓ Verification inconclusive

getPlayerData(UUID) may throw when player not present

.all().getFirst() assumes the query always returns a row; an empty result triggers NoSuchElementException.

-                .all()
-                .getFirst();
+                .all()
+                .stream()
+                .findFirst()
+                .orElse(null);

Return null (or Optional<PlayerData>) to let callers handle “not in DB” gracefully.


🏁 Script executed:

#!/bin/bash
# Locate Query class file
QUERY_FILE=$(rg -l "class Query" src/main/java | grep -m1 "Query.java")
echo "Query class file: $QUERY_FILE"

# Show the beginning of the Query class
sed -n '1,200p' "$QUERY_FILE"

# Inspect the 'all()' method signature/implementation
rg -n "all(" "$QUERY_FILE"

# Inspect the 'getFirst' method signature/implementation
rg -n "getFirst" "$QUERY_FILE"

Length of output: 321


🏁 Script executed:

#!/bin/bash
set -e

# Locate PlayerDataDatabase.java
PDB_FILE=$(rg -l "class PlayerDataDatabase" -g "*.java" src)
echo "PlayerDataDatabase file: $PDB_FILE"

# Show its imports and Query usage (first 50 lines)
echo "----- imports and class declaration -----"
sed -n '1,50p' "$PDB_FILE"

# Show the specific Query.query import line if present
echo "----- Query import line -----"
grep -n "import .*Query" "$PDB_FILE" || echo "No explicit import of Query found"

# Determine Query class path from package structure
QUERY_IMPORT=$(grep "import .*Query" "$PDB_FILE" | sed 's/import \(.*\);/\1/')
echo "Queried import: $QUERY_IMPORT"

if [ -n "$QUERY_IMPORT" ]; then
  QUERY_PATH="src/$(echo "$QUERY_IMPORT" | sed 's/\./\//g').java"
  echo "Guessing Query file at: $QUERY_PATH"
  if [ -f "$QUERY_PATH" ]; then
    echo "----- Start of Query.java -----"
    sed -n '1,200p' "$QUERY_PATH"
    echo "----- Searching for all( and getFirst in Query.java -----"
    grep -n "all(" "$QUERY_PATH" || echo "No all( found"
    grep -n "getFirst" "$QUERY_PATH" || echo "No getFirst found"
  else
    echo "Query.java not found at guessed path"
  fi
else
  echo "Skipping Query class inspection due to missing import"
fi

Length of output: 3143


Verify getFirst() behavior before handling empty results

  • In src/main/java/de/j/deathMinigames/database/PlayerDataDatabase.java (lines 81–96), calling
    .all().getFirst() will throw a NoSuchElementException if the query returns no rows.
  • Consider replacing with:
-                .all()
-                .getFirst();
+                .all()
+                .stream()
+                .findFirst()
+                .orElse(null);
  • Or change the method signature to Optional<PlayerData> and return
    .all().stream().findFirst() directly.

Please verify how getFirst() is implemented in your de.chojo.sadu.queries library and adjust the code so that “player not in DB” is handled gracefully.

src/main/java/de/j/deathMinigames/listeners/AnvilListener.java (1)

32-36: 🛠️ Refactor suggestion

⚠️ Potential issue

❗ Instance fields cause cross‑player data leaks

serverName, hostName, teamName and claimingRadius live as ordinary fields in a listener that is shared by every player.
When two players interact with an anvil in parallel the last writer wins – e.g. Alice’s teamName might be applied to Bob’s team.

Use local variables (or metadata attached to the AnvilView / Player) rather than mutable listener state.

- private String teamName;
- private int claimingRadius;
+ // remove – keep data per‑interaction instead
⛔ Skipped due to learnings
Learnt from: PrinzPizza42
PR: AtomPilz-DevTeam/StationofDoomPlugin#196
File: src/main/java/de/j/deathMinigames/listeners/AnvilListener.java:27-0
Timestamp: 2025-01-06T19:05:17.436Z
Learning: In the `AnvilListener` class (src/main/java/de/j/deathMinigames/listeners/AnvilListener.java), concurrency concerns for storing the `serverName` or `hostName` instance fields are considered non-problematic because only operators can access the relevant menus. Overwriting these fields does not impact the experience negatively.
src/main/java/de/j/deathMinigames/main/PlayerData.java (1)

124-131: isConnected() may not exist on all servers

Paper provides Player#isConnected(); Spigot/Bukkit only has isOnline(). If you aim for broader compatibility prefer isOnline() or feature‑detect the method.

@PrinzPizza42
Copy link
Collaborator Author

PrinzPizza42 commented Apr 21, 2025

The version number still has to be changed, and I need to change some things according to the suggested changes. Other than that there seems to be a problem with the README file, as there are some errors in the code I did not change, and I can therefore not commit the file.
image
Could you maybe look into this as it is your code and I am not sure how and what to change?

@JonasFranke JonasFranke force-pushed the Create-team-system branch from 8e5c344 to 794087f Compare June 1, 2025 10:00
@JonasFranke
Copy link
Collaborator

The version number still has to be changed, and I need to change some things according to the suggested changes. Other than that there seems to be a problem with the README file, as there are some errors in the code I did not change, and I can therefore not commit the file. image Could you maybe look into this as it is your code and I am not sure how and what to change?

I fixed the README.md and removed the failing tests (The repo server is down).

@JonasFranke
Copy link
Collaborator

After you look into CodeFactor and the CodeRabbit review, we can merge!

Copy link
Contributor

@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

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 8e5c344 and 794087f.

📒 Files selected for processing (3)
  • .github/workflows/gradle.yml (0 hunks)
  • .github/workflows/repo.yml (0 hunks)
  • README.md (2 hunks)
💤 Files with no reviewable changes (2)
  • .github/workflows/repo.yml
  • .github/workflows/gradle.yml
🧰 Additional context used
🪛 LanguageTool
README.md

[uncategorized] ~92-~92: A comma might be missing here.
Context: ...ch lets players create teams, to access it enter the command /teams. The teams a...

(AI_EN_LECTOR_MISSING_PUNCTUATION_COMMA)

🔇 Additional comments (1)
README.md (1)

168-168: Approve Maven repository URL update.

The <url> tag now correctly points to the intended repository endpoint.

PrinzPizza42 and others added 2 commits June 1, 2025 19:22
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
@AtomPilz-DevTeam AtomPilz-DevTeam deleted a comment from coderabbitai bot Jun 6, 2025
Copy link
Contributor

@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: 3

♻️ Duplicate comments (6)
src/main/java/de/j/stationofdoom/teams/TeamCMD.java (1)

14-16: Player null check properly implemented.

The code correctly checks if the sender is a Player instance before casting, which addresses the previous review comment.

src/main/java/de/j/deathMinigames/database/TeamsDatabase.java (1)

58-76: 🛠️ Refactor suggestion

N+1 query problem affects performance at scale.

The current implementation executes a separate query for each team to fetch its members. This creates N+1 queries for N teams, which can significantly impact performance during startup with many teams.

Consider fetching all team members in a single query and grouping them in memory:

 for (Team team : teams) {
     Main.getMainLogger().info(team.getName());
-    try {
-        Query.query("SELECT isInTeam, uuid, isTeamOperator FROM playerData WHERE uuidOfTeam = ?;")
-                .single(Call.of()
-                        .bind(team.getUuid(), UUIDAdapter.AS_STRING)
-                )
-                .map(row -> new DBTeamMember(
-                        row.getBoolean("isInTeam"),
-                        row.getString("uuid"),
-                        row.getBoolean("isTeamOperator")))
-                .all()
-                .forEach(team::addMember);
-    }
-    catch (Exception e) {
-        e.printStackTrace();
-        Main.getMainLogger().warning("Could not get players from team " + team.getName());
-    }
 }
+
+// Fetch all team members in one query
+Query.query("SELECT uuidOfTeam, isInTeam, uuid, isTeamOperator FROM playerData WHERE uuidOfTeam IS NOT NULL;")
+    .single()
+    .map(row -> new Object[]{
+        row.getString("uuidOfTeam"),
+        new DBTeamMember(
+            row.getBoolean("isInTeam"),
+            row.getString("uuid"),
+            row.getBoolean("isTeamOperator"))
+    })
+    .all()
+    .forEach(data -> {
+        String teamUuid = (String) data[0];
+        DBTeamMember member = (DBTeamMember) data[1];
+        teams.stream()
+            .filter(t -> t.getUuid().toString().equals(teamUuid))
+            .findFirst()
+            .ifPresent(team -> team.addMember(member));
+    });
src/main/java/de/j/stationofdoom/teams/TeamSettingsGUI.java (2)

24-24: Thread safety properly implemented.

Good use of ConcurrentHashMap to ensure thread safety for the invSlots map, which addresses the previous review comment.


108-110: Null material handling properly implemented.

The code correctly handles the case where material conversion returns null by providing a fallback to WHITE_STAINED_GLASS_PANE, which addresses the previous review comment.

src/main/java/de/j/deathMinigames/database/PlayerDataDatabase.java (1)

195-205: 🛠️ Refactor suggestion

Use EXISTS query for more efficient existence check.

The current implementation still fetches data unnecessarily. Use EXISTS for better performance and remove debug logging.

 public boolean checkIfPlayerIsInDatabase(UUID uuidOfPlayerToCheck) {
     if(!Database.getInstance().isConnected) return HandlePlayers.getKnownPlayers().containsKey(uuidOfPlayerToCheck);
-    List<UUID> data = Query.query("SELECT UUID FROM playerData WHERE UUID = :UUID;")
-            .single(Call.of()
-                    .bind("UUID", uuidOfPlayerToCheck, UUIDAdapter.AS_STRING))
-            .map(row -> UUID.fromString(row.getString("UUID")))
-            .all();
-
-    Main.getMainLogger().warning("CheckIfPlayerIsInDatabase for: " + Bukkit.getOfflinePlayer(uuidOfPlayerToCheck).getName() + ", found " + !data.isEmpty());
-    return !data.isEmpty();
+    return Query.query("SELECT 1 FROM playerData WHERE UUID = :UUID LIMIT 1;")
+            .single(Call.of()
+                    .bind("UUID", uuidOfPlayerToCheck, UUIDAdapter.AS_STRING))
+            .exists();
 }
src/main/java/de/j/deathMinigames/main/PlayerData.java (1)

74-82: Good error handling for offline players.

Based on our previous discussions, OfflinePlayer.getLocation() is indeed a valid method. The try-catch approach provides good fallback handling for when the player is offline.

🧹 Nitpick comments (13)
src/main/java/de/j/stationofdoom/teams/TeamCMD.java (1)

19-20: Consider making TranslationFactory a class field.

The TranslationFactory is instantiated inline each time it's needed. Consider making it a static or instance field for better performance.

 public class TeamCMD implements BasicCommand {
+    private static final TranslationFactory tf = new TranslationFactory();
+    
     @Override
     public void execute(CommandSourceStack stack, String[] args) {
         // ...
-        player.sendMessage(Component.text(new TranslationFactory().getTranslation(player, "TeamCMDNoTeam"))
+        player.sendMessage(Component.text(tf.getTranslation(player, "TeamCMDNoTeam"))
                 .color(NamedTextColor.RED));
src/main/java/de/j/deathMinigames/database/TeamsDatabase.java (1)

133-146: Consider using transactions to prevent data loss.

The current implementation wipes the entire database before re-inserting teams. If an error occurs during re-insertion, all team data could be lost. Consider using database transactions or incremental updates instead.

 public void updateTeamsDatabase() {
     if(!Database.getInstance().isConnected) return;
     List<Team> teams = HandleTeams.getInstance().getAllTeams();
-    wipeDatabase();
-    for (Team team : teams) {
-        if(team.getAllPlayers().isEmpty()) {
-            Main.getMainLogger().info("Team " + team.getName() + " has no players, removing it");
-        }
-        else {
-            insertTeam(team);
+    
+    // Consider implementing transaction support or:
+    // 1. Track which teams need to be inserted/updated/deleted
+    // 2. Perform incremental updates
+    // 3. Only delete teams that no longer exist
+    
+    try {
+        // Begin transaction if supported
+        wipeDatabase();
+        for (Team team : teams) {
+            if(team.getAllPlayers().isEmpty()) {
+                Main.getMainLogger().info("Team " + team.getName() + " has no players, removing it");
+            }
+            else {
+                insertTeam(team);
+            }
         }
+        // Commit transaction if supported
+    } catch (Exception e) {
+        // Rollback transaction if supported
+        Main.getMainLogger().severe("Failed to update teams database: " + e.getMessage());
+        throw e;
     }
src/main/java/de/j/deathMinigames/database/PlayerDataDatabase.java (2)

51-56: Consider adding indexes for better query performance.

The UUID column is frequently used in WHERE clauses throughout the codebase. Adding an index would improve query performance.

-Query.query("CREATE TABLE IF NOT EXISTS playerData (name VARCHAR(255), UUID VARCHAR(255), introduction BOOLEAN, usesPlugin BOOLEAN, difficulty INT, bestParkourTime FLOAT, isInTeam BOOLEAN, uuidOfTeam VARCHAR(255), isTeamOperator BOOLEAN);")
+Query.query("CREATE TABLE IF NOT EXISTS playerData (name VARCHAR(255), UUID VARCHAR(255), introduction BOOLEAN, usesPlugin BOOLEAN, difficulty INT, bestParkourTime FLOAT, isInTeam BOOLEAN, uuidOfTeam VARCHAR(255), isTeamOperator BOOLEAN, INDEX idx_uuid (UUID));")

71-71: Remove temporary debug logging.

The TODO comment indicates this warning log should be removed.

-Main.getMainLogger().warning("used getAllPlayerData"); //TODO remove
src/main/java/de/j/deathMinigames/listeners/AnvilListener.java (1)

74-85: Improve claiming radius validation to avoid duplicate messages and invalid zero value.

The current code can send the error message twice, and a radius of 0 doesn't make sense for claiming.

 try {
     claimingRadius = Integer.parseInt(renameText);
+    if(claimingRadius < 1 || claimingRadius > 256) {
+        player.sendMessage(Component.text(tf.getTranslation(player, "invalidAnvilInputClaimingRadius")));
+        claimingRadius = -1; // Invalid marker
+    }
 }
 catch(NumberFormatException e) {
     player.sendMessage(Component.text(tf.getTranslation(player, "invalidAnvilInputClaimingRadius")));
-    return;
+    claimingRadius = -1; // Invalid marker
 }
-if(claimingRadius <= 0 || claimingRadius > 256) {
-    player.sendMessage(Component.text(tf.getTranslation(player, "invalidAnvilInputClaimingRadius")));
-    return;
-}
src/main/java/de/j/stationofdoom/util/Tablist.java (1)

199-275: Refactor repetitive team assignment logic to reduce code duplication.

The current implementation repeats the same pattern for each rank. Consider extracting the common logic.

 private void setTeam(Player player) {
     assert !Main.isFolia();
     UUID playerUUID = player.getUniqueId();
+    
+    // Determine base team name
+    String baseTeamName;
+    if(playerRanks.containsKey(playerUUID)) {
+        switch (playerRanks.get(playerUUID)) {
+            case HOST -> baseTeamName = "0Host";
+            case ADMIN -> baseTeamName = "1Admin";
+            case DEVELOPER -> baseTeamName = "2Developer";
+            default -> baseTeamName = "3";
+        }
+    } else {
+        baseTeamName = "3";
+    }
+    
+    // Build full team name
+    StringBuilder teamName = new StringBuilder();
+    if(afkPlayers.contains(playerUUID)) {
+        teamName.append(baseTeamName.equals("3") ? "3AFK_" : baseTeamName.replace(baseTeamName.charAt(0) + "", baseTeamName.charAt(0) + "AFK_"));
+    } else {
+        teamName.append(baseTeamName).append(baseTeamName.equals("3") ? "_" : "");
+    }
+    
+    // Add team suffix if player is in a team
+    Team playerTeam = HandleTeams.getTeamFromPlayerUUID(playerUUID);
+    if(playerTeam.isMember(playerUUID)) {
+        teamName.append(playerTeam.getUuid());
+    } else if(teamName.toString().endsWith("_")) {
+        teamName.setLength(teamName.length() - 1); // Remove trailing underscore
+    }
+    
+    // Add player to team
+    var team = scoreboard.getTeam(teamName.toString());
+    if(team != null) {
+        team.addPlayer(player);
+    }
-    // Remove all the existing switch/if logic
src/main/java/de/j/deathMinigames/main/PlayerData.java (3)

144-156: Remove debug logging statements.

The debug logging statements should be removed before production deployment. These will clutter the logs and may expose internal state information.

 public boolean isInTeam() {
-    Main.getMainLogger().warning("Tried accessing isInTeam of " + this + " isInTeam = " + this.isInTeam); //TODO remove
     return this.isInTeam;
 }

 public void setInTeam(boolean isInTeam) {
     this.isInTeam = isInTeam;
-    Main.getMainLogger().warning("Set isInTeam of " + this + " to " + this.isInTeam); //TODO remove
 }

197-197: Remove debug logging from constructor.

This debug logging statement should be removed before production deployment.

 this.leftWhileProcessing = false;
-Main.getMainLogger().warning("created player data for player " + this.name + " " + this.uuid); //TODO remove

218-218: Remove debug logging from constructor.

This debug logging statement should be removed before production deployment.

 }
-Main.getMainLogger().warning("created player data for player " + this.name + " " + this.uuid + " via long method"); //TODO remove
src/main/java/de/j/stationofdoom/teams/Team.java (4)

51-96: Consider reducing constructor duplication.

Multiple constructors have similar initialization logic. Consider extracting common initialization patterns to reduce code duplication.

 public Team(String name, String colorAsString, boolean locked, String uuid, Location protectedLocation) {
-    init(name, UUID.fromString(uuid), Material.valueOf(colorAsString));
-    convertMaterialToString(this.colorAsMaterial);
-    this.locked = locked;
-    this.inventory.setContents(TeamEnderchestsDatabase.getInstance().getTeamEnderchest(this.getUuid()).getContents());
+    initWithDatabase(name, UUID.fromString(uuid), Material.valueOf(colorAsString), locked);
     if (protectedLocation != null && protectedLocation.getWorld() != null) {
         this.protectedLocation = protectedLocation;
     }
 }

+private void initWithDatabase(String name, UUID uuid, Material colorAsMaterial, boolean locked) {
+    init(name, uuid, colorAsMaterial);
+    convertMaterialToString(this.colorAsMaterial);
+    this.locked = locked;
+    this.inventory.setContents(TeamEnderchestsDatabase.getInstance().getTeamEnderchest(this.getUuid()).getContents());
+}

168-176: Improve random material selection readability.

The current implementation works but could be more readable and efficient.

 private Material getRandomConcreteMaterial() {
-    List<Material> materials = new ArrayList<>();
-    for (Material material : Material.values()) {
-        if (material.name().contains("CONCRETE") && !material.name().contains("POWDER")) {
-            materials.add(material);
-        }
-    }
-    return materials.get(ThreadLocalRandom.current().nextInt(0, materials.size()));
+    List<Material> concreteMaterials = Arrays.stream(Material.values())
+        .filter(material -> material.name().contains("CONCRETE") && !material.name().contains("POWDER"))
+        .collect(Collectors.toList());
+    
+    return concreteMaterials.get(ThreadLocalRandom.current().nextInt(concreteMaterials.size()));
 }

277-287: Simplify member checking logic.

The current implementation can be simplified using HashMap's containsKey method directly.

 public boolean isMember(UUID uuidOfPlayerToCheck) {
     if (uuidOfPlayerToCheck == null) {
         return false;
     }
-    for (UUID uuid : this.members.keySet()) {
-        if(uuid.equals(uuidOfPlayerToCheck)) {
-            return true;
-        }
-    }
-    return false;
+    return this.members.containsKey(uuidOfPlayerToCheck);
 }

260-270: Good operator management with edge case handling.

The operator management properly handles the case where no operators remain by unlocking the team. The redundant containsKey check in line 269 could be simplified since line 268 already checks this.

 public boolean isTeamOperator(PlayerData playerData) {
     if(!this.members.containsKey(playerData.getUniqueId())) return false;
-    return this.members.containsKey(playerData.getUniqueId()) && this.members.get(playerData.getUniqueId());
+    return this.members.get(playerData.getUniqueId());
 }
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 39d56e4 and 5143a93.

📒 Files selected for processing (21)
  • src/main/java/de/j/deathMinigames/database/DBTeamMember.java (1 hunks)
  • src/main/java/de/j/deathMinigames/database/PlayerDataDatabase.java (6 hunks)
  • src/main/java/de/j/deathMinigames/database/TeamEnderchestsDatabase.java (1 hunks)
  • src/main/java/de/j/deathMinigames/database/TeamsDatabase.java (1 hunks)
  • src/main/java/de/j/deathMinigames/listeners/AnvilListener.java (5 hunks)
  • src/main/java/de/j/deathMinigames/listeners/JoinListener.java (2 hunks)
  • src/main/java/de/j/deathMinigames/main/HandlePlayers.java (8 hunks)
  • src/main/java/de/j/deathMinigames/main/PlayerData.java (7 hunks)
  • src/main/java/de/j/deathMinigames/settings/AnvilUI.java (2 hunks)
  • src/main/java/de/j/stationofdoom/teams/HandleTeams.java (1 hunks)
  • src/main/java/de/j/stationofdoom/teams/Team.java (1 hunks)
  • src/main/java/de/j/stationofdoom/teams/TeamCMD.java (1 hunks)
  • src/main/java/de/j/stationofdoom/teams/TeamPlayerSettingsGUI.java (1 hunks)
  • src/main/java/de/j/stationofdoom/teams/TeamSettingsGUI.java (1 hunks)
  • src/main/java/de/j/stationofdoom/teams/TeamsMainMenuGUI.java (1 hunks)
  • src/main/java/de/j/stationofdoom/teams/chunkClaimSystem/BlockBreakAndUseCancelListener.java (1 hunks)
  • src/main/java/de/j/stationofdoom/teams/chunkClaimSystem/ChunkClaimSystem.java (1 hunks)
  • src/main/java/de/j/stationofdoom/teams/enderchest/EnderchestInvHolder.java (1 hunks)
  • src/main/java/de/j/stationofdoom/teams/enderchest/TeamEnderchestPreventEnchantedItemsInputListener.java (1 hunks)
  • src/main/java/de/j/stationofdoom/util/Tablist.java (2 hunks)
  • src/main/resources/translations.json (4 hunks)
✅ Files skipped from review due to trivial changes (1)
  • src/main/java/de/j/deathMinigames/database/DBTeamMember.java
🚧 Files skipped from review as they are similar to previous changes (12)
  • src/main/java/de/j/stationofdoom/teams/enderchest/EnderchestInvHolder.java
  • src/main/java/de/j/stationofdoom/teams/enderchest/TeamEnderchestPreventEnchantedItemsInputListener.java
  • src/main/java/de/j/stationofdoom/teams/chunkClaimSystem/BlockBreakAndUseCancelListener.java
  • src/main/java/de/j/deathMinigames/settings/AnvilUI.java
  • src/main/resources/translations.json
  • src/main/java/de/j/deathMinigames/listeners/JoinListener.java
  • src/main/java/de/j/stationofdoom/teams/TeamsMainMenuGUI.java
  • src/main/java/de/j/deathMinigames/main/HandlePlayers.java
  • src/main/java/de/j/stationofdoom/teams/TeamPlayerSettingsGUI.java
  • src/main/java/de/j/stationofdoom/teams/HandleTeams.java
  • src/main/java/de/j/deathMinigames/database/TeamEnderchestsDatabase.java
  • src/main/java/de/j/stationofdoom/teams/chunkClaimSystem/ChunkClaimSystem.java
🧰 Additional context used
🧠 Learnings (6)
src/main/java/de/j/deathMinigames/database/TeamsDatabase.java (1)
Learnt from: PrinzPizza42
PR: AtomPilz-DevTeam/StationofDoomPlugin#214
File: src/main/java/de/j/deathMinigames/database/TeamsDatabase.java:34-39
Timestamp: 2025-06-01T19:44:19.351Z
Learning: The chojo.sadu library used in this codebase only supports specific statement methods like `.insert()`, `.delete()`, etc. and does not have a generic `.execute()` method. For DDL statements like CREATE TABLE, `.insert()` is the appropriate method to use with this library.
src/main/java/de/j/deathMinigames/database/PlayerDataDatabase.java (1)
Learnt from: PrinzPizza42
PR: AtomPilz-DevTeam/StationofDoomPlugin#176
File: src/main/java/de/j/deathMinigames/minigames/Minigame.java:47-49
Timestamp: 2024-12-13T07:24:43.026Z
Learning: In `src/main/java/de/j/deathMinigames/minigames/Minigame.java`, `playerInArena` is always null-checked before comparing its UUID with `player.getUniqueId()`. Therefore, additional null checks for `playerInArena` before the UUID comparison are unnecessary.
src/main/java/de/j/stationofdoom/util/Tablist.java (2)
Learnt from: PrinzPizza42
PR: AtomPilz-DevTeam/StationofDoomPlugin#214
File: src/main/java/de/j/stationofdoom/util/Tablist.java:203-206
Timestamp: 2025-06-06T09:04:41.968Z
Learning: In the HandleTeams.getTeamFromPlayerUUID method, the method never returns null but instead returns either the player's actual team or a new empty Team() object when the player is not in any team. The subsequent isMember() check on an empty team returns false, safely handling the no-team case without causing NullPointerExceptions.
Learnt from: PrinzPizza42
PR: AtomPilz-DevTeam/StationofDoomPlugin#214
File: src/main/java/de/j/stationofdoom/util/Tablist.java:191-194
Timestamp: 2025-06-06T09:03:32.059Z
Learning: In the StationofDoomPlugin project, hard-coded UUIDs for developer ranks in the Tablist class are intentionally kept simple since there are only two developers and the values rarely change. The team prefers this approach over configuration-based management for this specific use case.
src/main/java/de/j/deathMinigames/listeners/AnvilListener.java (1)
Learnt from: PrinzPizza42
PR: AtomPilz-DevTeam/StationofDoomPlugin#214
File: src/main/java/de/j/stationofdoom/util/Tablist.java:203-206
Timestamp: 2025-06-06T09:04:41.968Z
Learning: In the HandleTeams.getTeamFromPlayerUUID method, the method never returns null but instead returns either the player's actual team or a new empty Team() object when the player is not in any team. The subsequent isMember() check on an empty team returns false, safely handling the no-team case without causing NullPointerExceptions.
src/main/java/de/j/stationofdoom/teams/Team.java (1)
Learnt from: PrinzPizza42
PR: AtomPilz-DevTeam/StationofDoomPlugin#214
File: src/main/java/de/j/stationofdoom/teams/Team.java:214-243
Timestamp: 2025-06-06T10:12:40.804Z
Learning: In the StationofDoomPlugin project, the TeamEnderchestsDatabase automatically cleans itself up during server shutdown, so manual cleanup calls like `TeamEnderchestsDatabase.getInstance().removeTeamEnderchest()` are not necessary in team removal methods.
src/main/java/de/j/deathMinigames/main/PlayerData.java (1)
Learnt from: PrinzPizza42
PR: AtomPilz-DevTeam/StationofDoomPlugin#214
File: src/main/java/de/j/deathMinigames/main/PlayerData.java:74-82
Timestamp: 2025-06-04T11:42:55.996Z
Learning: OfflinePlayer in the Spigot API has a getLocation() method that returns the player's current location or null if the player has never played before. This method is valid and will compile.
🔇 Additional comments (6)
src/main/java/de/j/stationofdoom/teams/TeamCMD.java (1)

17-18: ⚠️ Potential issue

Add null check for team to prevent NPE.

The HandleTeams.getTeamFromPlayerUUID method could return null, which would cause a NullPointerException when calling isMember().

 Team team = HandleTeams.getTeamFromPlayerUUID(player.getUniqueId());
+if (team == null) {
+    player.sendMessage(Component.text(new TranslationFactory().getTranslation(player, "TeamCMDNoTeam"))
+            .color(NamedTextColor.RED));
+    return;
+}
 if (!team.isMember(player.getUniqueId())) {
⛔ Skipped due to learnings
Learnt from: PrinzPizza42
PR: AtomPilz-DevTeam/StationofDoomPlugin#214
File: src/main/java/de/j/stationofdoom/util/Tablist.java:203-206
Timestamp: 2025-06-06T09:04:41.968Z
Learning: In the HandleTeams.getTeamFromPlayerUUID method, the method never returns null but instead returns either the player's actual team or a new empty Team() object when the player is not in any team. The subsequent isMember() check on an empty team returns false, safely handling the no-team case without causing NullPointerExceptions.
Learnt from: PrinzPizza42
PR: AtomPilz-DevTeam/StationofDoomPlugin#214
File: src/main/java/de/j/stationofdoom/teams/TeamsCMD.java:24-28
Timestamp: 2025-06-01T17:13:58.721Z
Learning: In the TeamsCMD class for team enderchest access via command, the silent return when a player is not in a team or the team is null is intentional defensive programming against bugs, not user-facing functionality that requires feedback messages.
src/main/java/de/j/deathMinigames/main/PlayerData.java (3)

23-29: Good method rename for consistency.

The rename from getUuid() to getUniqueId() improves consistency with Bukkit API naming conventions. The addition of setUniqueID() provides the needed setter functionality.


31-36: Excellent lazy initialization pattern.

The lazy initialization of the player field improves performance by only fetching the Player object when needed. Good defensive programming practice.


200-217: Good team data initialization logic.

The constructor properly handles team data initialization with appropriate null checks and UUID conversion. The conditional logic for team membership is well-structured.

src/main/java/de/j/stationofdoom/teams/Team.java (2)

214-258: Good error handling in team removal.

The team removal logic properly handles edge cases and includes exception handling for item dropping. The inventory clearing at the end helps with garbage collection. Based on previous feedback, the database cleanup is handled automatically on server shutdown.


182-197: Complex but correct member join/leave logic.

The logic properly handles both joining and leaving scenarios, including team removal when the last member leaves. The integration with PlayerData updates is well-structured.

Copy link
Contributor

@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 (2)
src/main/java/de/j/stationofdoom/teams/chunkClaimSystem/ChunkClaimSystem.java (1)

172-193: ⚠️ Potential issue

Bukkit API used from async thread – can crash the server

The method uses runTaskTimerAsynchronously but calls player.spawnParticle which is not thread-safe. This can cause crashes or unpredictable behavior.

-        runnable.runTaskTimerAsynchronously(Main.getPlugin(), 0, period);
+        runnable.runTaskTimer(Main.getPlugin(), 0, period);
src/main/java/de/j/deathMinigames/database/PlayerDataDatabase.java (1)

85-101: ⚠️ Potential issue

Handle empty query results to prevent NoSuchElementException.

The getFirst() call will throw an exception if no player data is found.

 public PlayerData getPlayerDataFromDB(UUID uuid) {
     if(!Database.getInstance().isConnected) return null;
-    return Query.query("SELECT * FROM playerdata WHERE uuid = ?;")
+    List<PlayerData> results = Query.query("SELECT * FROM playerdata WHERE uuid = ?;")
             .single(Call.of()
                     .bind(uuid, UUIDAdapter.AS_STRING))
             .map(row -> new PlayerData(row.getString("name"),
                     row.getString("uuid"),
                     row.getBoolean("introduction"),
                     row.getBoolean("usesPlugin"),
                     row.getInt("difficulty"),
                     row.getFloat("bestParkourTime"),
                     row.getBoolean("isInTeam"),
                     row.getString("uuidOfTeam"),
                     row.getBoolean("isTeamOperator")))
-            .all()
-            .getFirst();
+            .all();
+    return results.isEmpty() ? null : results.get(0);
 }
🧹 Nitpick comments (2)
src/main/java/de/j/stationofdoom/teams/chunkClaimSystem/ChunkClaimSystem.java (1)

91-102: Remove unused variable i.

The variable i is incremented but never used.

 private HashMap<Location, Team> getAllProtectedLocations() {
     HashMap<Location, Team> locationsOfTeamsMap = new HashMap<>();
-    int i = 0;
     for (Team team : HandleTeams.getInstance().getAllTeams()){
         Location location = team.getProtectedLocation();
         if(location != null) {
             locationsOfTeamsMap.put(team.getProtectedLocation(), team);
-            i++;
         }
     }
     return locationsOfTeamsMap;
 }
src/main/java/de/j/deathMinigames/database/PlayerDataDatabase.java (1)

193-202: Remove debug logging statement.

The warning log on line 201 appears to be debug code that should be removed.

-        Main.getMainLogger().warning("CheckIfPlayerIsInDatabase for: " + Bukkit.getOfflinePlayer(uuidOfPlayerToCheck).getName() + ", found " + !data.isEmpty());
         return !data.isEmpty();
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 5143a93 and 3fde6ae.

📒 Files selected for processing (6)
  • src/main/java/de/j/deathMinigames/database/PlayerDataDatabase.java (6 hunks)
  • src/main/java/de/j/deathMinigames/listeners/JoinListener.java (2 hunks)
  • src/main/java/de/j/deathMinigames/main/HandlePlayers.java (8 hunks)
  • src/main/java/de/j/deathMinigames/main/PlayerData.java (7 hunks)
  • src/main/java/de/j/stationofdoom/teams/TeamSettingsInventoryListener.java (1 hunks)
  • src/main/java/de/j/stationofdoom/teams/chunkClaimSystem/ChunkClaimSystem.java (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/main/java/de/j/deathMinigames/listeners/JoinListener.java
  • src/main/java/de/j/deathMinigames/main/HandlePlayers.java
  • src/main/java/de/j/deathMinigames/main/PlayerData.java
🧰 Additional context used
🧠 Learnings (2)
src/main/java/de/j/deathMinigames/database/PlayerDataDatabase.java (1)
Learnt from: PrinzPizza42
PR: AtomPilz-DevTeam/StationofDoomPlugin#176
File: src/main/java/de/j/deathMinigames/minigames/Minigame.java:47-49
Timestamp: 2024-12-13T07:24:43.026Z
Learning: In `src/main/java/de/j/deathMinigames/minigames/Minigame.java`, `playerInArena` is always null-checked before comparing its UUID with `player.getUniqueId()`. Therefore, additional null checks for `playerInArena` before the UUID comparison are unnecessary.
src/main/java/de/j/stationofdoom/teams/TeamSettingsInventoryListener.java (1)
Learnt from: PrinzPizza42
PR: AtomPilz-DevTeam/StationofDoomPlugin#214
File: src/main/java/de/j/stationofdoom/teams/TeamSettingsInventoryListener.java:31-50
Timestamp: 2025-06-04T11:47:04.808Z
Learning: In the StationofDoomPlugin project, the team operates in a controlled environment where they manage inventory creation through their own plugin, and server owners actively choose which plugins to install. The team considers this sufficient protection against potential issues with inventory manipulation from external sources.
🔇 Additional comments (4)
src/main/java/de/j/stationofdoom/teams/chunkClaimSystem/ChunkClaimSystem.java (1)

29-38: Well-implemented thread-safe singleton pattern.

The double-checked locking with volatile instance ensures thread safety and lazy initialization.

src/main/java/de/j/deathMinigames/database/PlayerDataDatabase.java (3)

53-53: Table schema properly updated for team support.

All necessary team-related columns (isInTeam, uuidOfTeam, isTeamOperator) are included.


119-145: Well-structured team data update logic.

The method correctly handles both scenarios - players with and without teams, ensuring data consistency.


166-176: INSERT query correctly includes all columns.

The previously missing isTeamOperator column is now properly included in the INSERT statement.

Copy link
Contributor

@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)
src/main/java/de/j/stationofdoom/teams/TeamSettingsInventoryListener.java (1)

37-39: Null check occurs after variable usage.

The player is cast and used to get PlayerData before the null check.

🧹 Nitpick comments (3)
src/main/java/de/j/stationofdoom/teams/TeamSettingsInventoryListener.java (3)

168-169: Remove unnecessary null check for player UUID.

Player.getUniqueId() never returns null in Bukkit/Paper APIs, making this check redundant.

-            if(player.getUniqueId() == null) {
-                Main.getMainLogger().info("Player is null in TeamSettingsInventoryListener");
-                return;
-            }

178-184: Simplify complex conditional logic.

The nested conditions can be simplified for better readability.

-        if(slot >= 9 && slot <=13 || slot == 17 || slot >= 18 && slot <= 44 && inv.getItem(slot) != null) {
-            if(team.getLocked() && !team.isTeamOperator(HandlePlayers.getInstance().getPlayerData(player.getUniqueId()))) {
-                player.sendMessage(Component.text(tf.getTranslation(player, "teamLockedAndNotOperator")).color(NamedTextColor.RED));
-                player.playSound(Sound.sound(BLOCK_AMETHYST_CLUSTER_BREAK, Sound.Source.PLAYER, 0.5F, 1), net.kyori.adventure.sound.Sound.Emitter.self());
-                teamSettingsGUI.showPage(currentPage, player);
-            }
-        }
+        boolean isRestrictedSlot = (slot >= 9 && slot <= 13) || slot == 17 || 
+                                  (slot >= 18 && slot <= 44 && inv.getItem(slot) != null);
+        if(isRestrictedSlot && team.getLocked() && !team.isTeamOperator(HandlePlayers.getInstance().getPlayerData(player.getUniqueId()))) {
+            player.sendMessage(Component.text(tf.getTranslation(player, "teamLockedAndNotOperator")).color(NamedTextColor.RED));
+            player.playSound(Sound.sound(BLOCK_AMETHYST_CLUSTER_BREAK, Sound.Source.PLAYER, 0.5F, 1), net.kyori.adventure.sound.Sound.Emitter.self());
+            teamSettingsGUI.showPage(currentPage, player);
+            return;
+        }

264-267: Extract duplicate permission checking logic.

The team lock and operator permission check is duplicated across multiple methods.

+    private boolean hasPermissionForLockedTeam(Team team, Player player) {
+        return !team.getLocked() || team.isTeamOperator(HandlePlayers.getInstance().getPlayerData(player.getUniqueId()));
+    }
+    
     if(slot == 9 || slot == 10) {
-        if(!team.isTeamOperator(playerData) && team.getLocked()) {
+        if(!hasPermissionForLockedTeam(team, player)) {
             player.sendMessage(Component.text(tf.getTranslation(player, "teamLockedAndNotOperator")).color(NamedTextColor.RED));
             return;
         }
     }
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 3fde6ae and 31210b9.

📒 Files selected for processing (3)
  • src/main/java/de/j/deathMinigames/database/PlayerDataDatabase.java (6 hunks)
  • src/main/java/de/j/deathMinigames/main/HandlePlayers.java (8 hunks)
  • src/main/java/de/j/stationofdoom/teams/TeamSettingsInventoryListener.java (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/main/java/de/j/deathMinigames/main/HandlePlayers.java
  • src/main/java/de/j/deathMinigames/database/PlayerDataDatabase.java
🧰 Additional context used
🧠 Learnings (1)
src/main/java/de/j/stationofdoom/teams/TeamSettingsInventoryListener.java (2)
Learnt from: PrinzPizza42
PR: AtomPilz-DevTeam/StationofDoomPlugin#214
File: src/main/java/de/j/stationofdoom/teams/TeamSettingsInventoryListener.java:31-50
Timestamp: 2025-06-04T11:47:04.808Z
Learning: In the StationofDoomPlugin project, the team operates in a controlled environment where they manage inventory creation through their own plugin, and server owners actively choose which plugins to install. The team considers this sufficient protection against potential issues with inventory manipulation from external sources.
Learnt from: PrinzPizza42
PR: AtomPilz-DevTeam/StationofDoomPlugin#214
File: src/main/java/de/j/stationofdoom/teams/TeamSettingsInventoryListener.java:232-240
Timestamp: 2025-06-06T11:30:50.234Z
Learning: In plugin-controlled inventories where the plugin systematically populates items and players cannot modify the contents, defensive null checks for inventory items may be unnecessary since the plugin guarantees the items will be present.
🔇 Additional comments (1)
src/main/java/de/j/stationofdoom/teams/TeamSettingsInventoryListener.java (1)

292-294: 🛠️ Refactor suggestion

Verify player exists before accessing methods.

The code assumes the player exists when getting by UUID, which could cause NPE if the player is offline.

         if(clickedOnPlayerData.isOnline()) {
-            clickedOnPlayerData.getPlayer().sendMessage(Component.text(tf.getTranslation(clickedOnPlayerData.getPlayer(), "kickedFromTeam", player.getName())).color(NamedTextColor.RED));
-            Bukkit.getPlayer(clickedOnPlayerData.getUniqueId()).playSound(Sound.sound(BLOCK_AMETHYST_BLOCK_RESONATE, Sound.Source.PLAYER, 1F, 1), net.kyori.adventure.sound.Sound.Emitter.self());
+            Player kickedPlayer = clickedOnPlayerData.getPlayer();
+            if (kickedPlayer != null) {
+                kickedPlayer.sendMessage(Component.text(tf.getTranslation(kickedPlayer, "kickedFromTeam", player.getName())).color(NamedTextColor.RED));
+                kickedPlayer.playSound(Sound.sound(BLOCK_AMETHYST_BLOCK_RESONATE, Sound.Source.PLAYER, 1F, 1), net.kyori.adventure.sound.Sound.Emitter.self());
+            }
         }
⛔ Skipped due to learnings
Learnt from: PrinzPizza42
PR: AtomPilz-DevTeam/StationofDoomPlugin#176
File: src/main/java/de/j/deathMinigames/settings/GUI.java:43-43
Timestamp: 2024-11-10T20:12:15.303Z
Learning: In `GUI.java`, the `getPlayer` method will return the player if they exist and should not return `null` or an `OfflinePlayer` if the player is offline. Therefore, redundant null checks on `getPlayer` are unnecessary in this context.

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.

Create team system

2 participants