diff --git a/.gradle/8.8/executionHistory/executionHistory.bin b/.gradle/8.8/executionHistory/executionHistory.bin index 98058b6..115517d 100644 Binary files a/.gradle/8.8/executionHistory/executionHistory.bin and b/.gradle/8.8/executionHistory/executionHistory.bin differ diff --git a/.gradle/8.8/executionHistory/executionHistory.lock b/.gradle/8.8/executionHistory/executionHistory.lock index 9a696ad..269f36e 100644 Binary files a/.gradle/8.8/executionHistory/executionHistory.lock and b/.gradle/8.8/executionHistory/executionHistory.lock differ diff --git a/.gradle/8.8/fileHashes/fileHashes.bin b/.gradle/8.8/fileHashes/fileHashes.bin index 5ab0aa5..a063853 100644 Binary files a/.gradle/8.8/fileHashes/fileHashes.bin and b/.gradle/8.8/fileHashes/fileHashes.bin differ diff --git a/.gradle/8.8/fileHashes/fileHashes.lock b/.gradle/8.8/fileHashes/fileHashes.lock index 2732f42..9c366dc 100644 Binary files a/.gradle/8.8/fileHashes/fileHashes.lock and b/.gradle/8.8/fileHashes/fileHashes.lock differ diff --git a/.gradle/8.8/fileHashes/resourceHashesCache.bin b/.gradle/8.8/fileHashes/resourceHashesCache.bin index baf3a03..a0c134a 100644 Binary files a/.gradle/8.8/fileHashes/resourceHashesCache.bin and b/.gradle/8.8/fileHashes/resourceHashesCache.bin differ diff --git a/.gradle/buildOutputCleanup/buildOutputCleanup.lock b/.gradle/buildOutputCleanup/buildOutputCleanup.lock index 1678000..b81338f 100644 Binary files a/.gradle/buildOutputCleanup/buildOutputCleanup.lock and b/.gradle/buildOutputCleanup/buildOutputCleanup.lock differ diff --git a/.gradle/buildOutputCleanup/outputFiles.bin b/.gradle/buildOutputCleanup/outputFiles.bin index 08bbe90..055d6f5 100644 Binary files a/.gradle/buildOutputCleanup/outputFiles.bin and b/.gradle/buildOutputCleanup/outputFiles.bin differ diff --git a/.gradle/file-system.probe b/.gradle/file-system.probe index eb4f76e..d4fe1fd 100644 Binary files a/.gradle/file-system.probe and b/.gradle/file-system.probe differ diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..3908719 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml index 526e09f..26a7d9b 100644 --- a/.idea/modules.xml +++ b/.idea/modules.xml @@ -2,7 +2,7 @@ - + \ No newline at end of file diff --git a/.idea/modules/me.freezy.plugins.papermc.BlazeSMP.main.iml b/.idea/modules/me.freezy.plugins.papermc.BlazeSMP.main.iml new file mode 100644 index 0000000..bbeeb3e --- /dev/null +++ b/.idea/modules/me.freezy.plugins.papermc.BlazeSMP.main.iml @@ -0,0 +1,14 @@ + + + + + + + PAPER + ADVENTURE + + 1 + + + + \ No newline at end of file diff --git a/src/main/java/me/freezy/plugins/papermc/blazesmp/command/ClanCommand.java b/src/main/java/me/freezy/plugins/papermc/blazesmp/command/ClanCommand.java index dfb63f4..bcde620 100644 --- a/src/main/java/me/freezy/plugins/papermc/blazesmp/command/ClanCommand.java +++ b/src/main/java/me/freezy/plugins/papermc/blazesmp/command/ClanCommand.java @@ -4,7 +4,9 @@ import me.freezy.plugins.papermc.blazesmp.BlazeSMP; import me.freezy.plugins.papermc.blazesmp.command.util.SimpleCommand; import me.freezy.plugins.papermc.blazesmp.module.Clan; import me.freezy.plugins.papermc.blazesmp.module.manager.Clans; +import net.kyori.adventure.text.Component; import org.bukkit.Bukkit; +import org.bukkit.OfflinePlayer; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; @@ -15,144 +17,953 @@ import java.util.*; import java.util.stream.Collectors; import java.util.stream.Stream; +import static net.kyori.adventure.text.minimessage.MiniMessage.miniMessage; + public class ClanCommand extends SimpleCommand { - private final BlazeSMP plugin; private final Clans clans; - private final LinkedHashMap> clanInvites = new LinkedHashMap<>(); + // Mapping: Clan -> Liste der Join-Anfragen (Spieler, die einer bestehenden Clan beitreten möchten) private final LinkedHashMap> clanJoins = new LinkedHashMap<>(); + // Mapping: Clan -> Liste der Einladungen (Spieler, die vom Clan eingeladen wurden) + private final LinkedHashMap> clanInvites = new LinkedHashMap<>(); public ClanCommand() { super("clan"); - plugin = BlazeSMP.getInstance(); + BlazeSMP plugin = BlazeSMP.getInstance(); clans = plugin.getClans(); } @Override - public boolean onCommand(@NotNull CommandSender sender, @NotNull Command cmd, @NotNull String list, @NotNull String[] args) { - return false; + public boolean onCommand(@NotNull CommandSender sender, + @NotNull Command cmd, + @NotNull String label, + @NotNull String[] args) { + if (!(sender instanceof Player player)) { + sender.sendMessage(miniMessage().deserialize("You must be a player to execute this command!")); + return true; + } + UUID playerUUID = player.getUniqueId(); + + // Keine Subcommands -> zeige Hilfe + if (args.length == 0) { + sendHelpMessage(player, playerUUID); + return true; + } + + // Verarbeitung der Unterbefehle + String subCommand = args[0].toLowerCase(); + switch (subCommand) { + + // ========== CREATE ========== + case "create" -> { + if (clans.isInClan(playerUUID)) { + player.sendMessage(miniMessage().deserialize("You are already in a clan!")); + return true; + } + if (args.length < 3) { + player.sendMessage(miniMessage().deserialize("Usage: /clan create ")); + return true; + } + String clanName = args[1]; + String clanTag = args[2]; + Component tagComponent = miniMessage().deserialize(clanTag); + + Clan newClan = new Clan(clanName, tagComponent, playerUUID); + // Clan hinzufügen und speichern + clans.addClan(newClan); + newClan.save(); + + player.sendMessage(miniMessage().deserialize("Clan created successfully!")); + return true; + } + + // ========== JOIN ========== + case "join" -> { + if (clans.isInClan(playerUUID)) { + player.sendMessage(miniMessage().deserialize("You are already in a clan!")); + return true; + } + if (args.length < 2) { + player.sendMessage(miniMessage().deserialize("Usage: /clan join ")); + return true; + } + String targetClanName = args[1]; + Clan targetClan = clans.getClanByName(targetClanName); + if (targetClan == null) { + player.sendMessage(miniMessage().deserialize("Clan not found!")); + return true; + } + + clanJoins.computeIfAbsent(targetClan, k -> new LinkedList<>()); + LinkedList joinRequests = clanJoins.get(targetClan); + + if (joinRequests.contains(playerUUID)) { + player.sendMessage(miniMessage().deserialize("You have already requested to join this clan.")); + return true; + } + joinRequests.add(playerUUID); + player.sendMessage(miniMessage().deserialize( + String.format("Join request sent to clan %s!", targetClan.getName()))); + + // Benachrichtige den Clan-Leader (sofern online) + Player leader = Bukkit.getPlayer(targetClan.getLeaderUUID()); + if (leader != null && leader.isOnline()) { + String acceptCommand = "/clan accept " + player.getName(); + String denyCommand = "/clan deny " + player.getName(); + Component notifyMsg = miniMessage().deserialize( + String.format("New join request from %s.\n", player.getName()) + + String.format("[Accept] ", acceptCommand) + + String.format("[Deny]", denyCommand) + ); + leader.sendMessage(notifyMsg); + } + return true; + } + + // ========== INVITE ========== + case "invite" -> { + if (!clans.isLeader(playerUUID) && !clans.isVice(playerUUID)) { + player.sendMessage(miniMessage().deserialize("You are not authorized to invite players to a clan!")); + return true; + } + if (args.length < 2) { + player.sendMessage(miniMessage().deserialize("Usage: /clan invite ")); + return true; + } + String inviteeName = args[1]; + Player invitee = Bukkit.getPlayer(inviteeName); + if (invitee == null || !invitee.isOnline()) { + player.sendMessage(miniMessage().deserialize(String.format("Player %s is not online!", inviteeName))); + return true; + } + if (clans.isInClan(invitee.getUniqueId())) { + player.sendMessage(miniMessage().deserialize(String.format("%s is already in a clan!", inviteeName))); + return true; + } + Clan inviterClan = clans.getClanByMember(playerUUID); + if (inviterClan == null) { + player.sendMessage(miniMessage().deserialize("Error: Your clan could not be found.")); + return true; + } + clanInvites.computeIfAbsent(inviterClan, k -> new LinkedList<>()); + LinkedList inviteList = clanInvites.get(inviterClan); + + if (inviteList.contains(invitee.getUniqueId())) { + player.sendMessage(miniMessage().deserialize(String.format("Player %s has already been invited!", inviteeName))); + return true; + } + inviteList.add(invitee.getUniqueId()); + player.sendMessage(miniMessage().deserialize(String.format("Invite sent to %s.", inviteeName))); + + // Benachrichtige den Eingeladenen + String acceptCmd = "/clan accept " + inviterClan.getName(); + String denyCmd = "/clan deny " + inviterClan.getName(); + Component inviteNotify = miniMessage().deserialize( + String.format("Invite from clan %s.\n", inviterClan.getName()) + + String.format("[Accept] ", acceptCmd) + + String.format("[Deny]", denyCmd) + ); + invitee.sendMessage(inviteNotify); + return true; + } + + // ========== ACCEPT ========== + case "accept" -> { + // 1) Spieler ist noch in keinem Clan -> Einladung annehmen + if (!clans.isInClan(playerUUID)) { + if (args.length < 2) { + player.sendMessage(miniMessage().deserialize("Usage: /clan accept ")); + return true; + } + String clanNameForInvite = args[1]; + Clan invitedClan = null; + for (Map.Entry> entry : clanInvites.entrySet()) { + if (entry.getValue().contains(playerUUID) + && entry.getKey().getName().equalsIgnoreCase(clanNameForInvite)) { + invitedClan = entry.getKey(); + break; + } + } + if (invitedClan == null) { + player.sendMessage(miniMessage().deserialize(String.format("No invite found from clan %s.", clanNameForInvite))); + return true; + } + invitedClan.getMembers().add(playerUUID); + clanInvites.get(invitedClan).remove(playerUUID); + + player.sendMessage(miniMessage().deserialize( + String.format("You have joined the clan %s!", invitedClan.getName()))); + + Player leader = Bukkit.getPlayer(invitedClan.getLeaderUUID()); + if (leader != null && leader.isOnline()) { + leader.sendMessage(miniMessage().deserialize( + String.format("%s has accepted the clan invite.", player.getName()))); + } + invitedClan.save(); + } else { + // 2) Spieler ist bereits in einem Clan -> Beitrittsanfrage annehmen (Leader/Vice) + if (!clans.isLeader(playerUUID) && !clans.isVice(playerUUID)) { + player.sendMessage(miniMessage().deserialize("You are not authorized to accept join requests.")); + return true; + } + if (args.length < 2) { + player.sendMessage(miniMessage().deserialize("Usage: /clan accept ")); + return true; + } + String joinRequesterName = args[1]; + Clan currentClan = clans.getClanByMember(playerUUID); + if (currentClan == null) { + player.sendMessage(miniMessage().deserialize("Error: Your clan could not be found.")); + return true; + } + LinkedList joinReqs = clanJoins.get(currentClan); + if (joinReqs == null || joinReqs.isEmpty()) { + player.sendMessage(miniMessage().deserialize("No join requests available.")); + return true; + } + UUID requesterUUID = getUuidByName(joinReqs, joinRequesterName); + if (requesterUUID == null) { + player.sendMessage(miniMessage().deserialize(String.format("No join request found from %s.", joinRequesterName))); + return true; + } + currentClan.getMembers().add(requesterUUID); + joinReqs.remove(requesterUUID); + + player.sendMessage(miniMessage().deserialize( + String.format("You have accepted %s's join request.", joinRequesterName))); + + Player requester = Bukkit.getPlayer(requesterUUID); + if (requester != null && requester.isOnline()) { + requester.sendMessage(miniMessage().deserialize( + String.format("Your join request for clan %s has been accepted.", currentClan.getName()))); + } + currentClan.save(); + } + return true; + } + + // ========== DENY ========== + case "deny" -> { + // 1) Spieler ist noch in keinem Clan -> Einladung ablehnen + if (!clans.isInClan(playerUUID)) { + if (args.length < 2) { + player.sendMessage(miniMessage().deserialize("Usage: /clan deny ")); + return true; + } + String clanNameForInvite = args[1]; + Clan invitedClan = null; + for (Map.Entry> entry : clanInvites.entrySet()) { + if (entry.getValue().contains(playerUUID) + && entry.getKey().getName().equalsIgnoreCase(clanNameForInvite)) { + invitedClan = entry.getKey(); + break; + } + } + if (invitedClan == null) { + player.sendMessage(miniMessage().deserialize(String.format("No invite found from clan %s.", clanNameForInvite))); + return true; + } + clanInvites.get(invitedClan).remove(playerUUID); + player.sendMessage(miniMessage().deserialize( + String.format("You have declined the clan invite from %s.", invitedClan.getName()))); + + Player leader = Bukkit.getPlayer(invitedClan.getLeaderUUID()); + if (leader != null && leader.isOnline()) { + leader.sendMessage(miniMessage().deserialize( + String.format("%s has declined the clan invite.", player.getName()))); + } + } else { + // 2) Spieler ist in einem Clan -> Beitrittsanfrage ablehnen (Leader/Vice) + if (!clans.isLeader(playerUUID) && !clans.isVice(playerUUID)) { + player.sendMessage(miniMessage().deserialize("You are not authorized to deny join requests.")); + return true; + } + if (args.length < 2) { + player.sendMessage(miniMessage().deserialize("Usage: /clan deny ")); + return true; + } + String joinRequesterName = args[1]; + Clan currentClan = clans.getClanByMember(playerUUID); + if (currentClan == null) { + player.sendMessage(miniMessage().deserialize("Error: Your clan could not be found.")); + return true; + } + LinkedList joinReqs = clanJoins.get(currentClan); + if (joinReqs == null || joinReqs.isEmpty()) { + player.sendMessage(miniMessage().deserialize("No join requests available.")); + return true; + } + UUID requesterUUID = getUuidByName(joinReqs, joinRequesterName); + if (requesterUUID == null) { + player.sendMessage(miniMessage().deserialize(String.format("No join request found from %s.", joinRequesterName))); + return true; + } + joinReqs.remove(requesterUUID); + + player.sendMessage(miniMessage().deserialize( + String.format("You have denied %s's join request.", joinRequesterName))); + + Player requester = Bukkit.getPlayer(requesterUUID); + if (requester != null && requester.isOnline()) { + requester.sendMessage(miniMessage().deserialize( + String.format("Your join request for clan %s has been denied.", currentClan.getName()))); + } + } + return true; + } + + // ========== LIST ========== + case "list" -> { + // Zeige eine Liste aller existierenden Clans an + sendClanList(player); + return true; + } + + // ========== INFO ========== + case "info" -> { + if (!clans.isInClan(playerUUID)) { + player.sendMessage(miniMessage().deserialize("You are not in a clan!")); + return true; + } + Clan clan = clans.getClanByMember(playerUUID); + if (clan == null) { + player.sendMessage(miniMessage().deserialize("Could not find your clan.")); + return true; + } + + // Sammle die Daten + UUID leaderUUID = clan.getLeaderUUID(); + UUID viceUUID = clan.getViceUUID(); + List members = clan.getMembers(); + + OfflinePlayer leaderOffline = Bukkit.getOfflinePlayer(leaderUUID); + String leaderName = leaderOffline.getName() != null ? leaderOffline.getName() : leaderUUID.toString(); + + String viceName = ""; + if (viceUUID != null) { + OfflinePlayer viceOffline = Bukkit.getOfflinePlayer(viceUUID); + viceName = (viceOffline.getName() != null) ? viceOffline.getName() : viceUUID.toString(); + } + + // Wir bauen den Info-Text in mehreren Component-Teilen auf + Component infoComponent = Component.empty(); + + // Header + Component l1 = miniMessage().deserialize("=== Clan info ===\n"); + // ID + Component l2 = miniMessage().deserialize(String.format( + " - ID: %s\n", + clan.getUuid() + )); + // Name + Component l3 = miniMessage().deserialize(String.format( + " - Name: %s\n", + clan.getName() + )); + // Leader + Component l4 = miniMessage().deserialize(String.format( + " - Leader: %s\n", + leaderName + )); + // Vice + Component l5 = miniMessage().deserialize(String.format( + " - Vice Leader: %s\n", + viceName + )); + // Member Count + Component l6 = miniMessage().deserialize(String.format( + " - Members: %d\n", + members.size() + )); + // Tag + Component l8 = miniMessage().deserialize(String.format( + " - Tag: %s\n", + miniMessage().serialize(clan.getTag()) + )); + // Member-Liste + Component l7 = miniMessage().deserialize("Members List:\n"); + + infoComponent = infoComponent + .append(l1).append(l2).append(l3) + .append(l4).append(l5).append(l6) + .append(l8).append(l7); + + // Detaillierte Auflistung der Mitglieder + for (UUID mem : members) { + OfflinePlayer off = Bukkit.getOfflinePlayer(mem); + String name = off.getName() != null ? off.getName() : mem.toString(); + Component memberLine = miniMessage().deserialize(String.format( + " - Member: %s\n", + name + )); + infoComponent = infoComponent.append(memberLine); + } + + // Abschlusslinie + Component endLine = miniMessage().deserialize("====================="); + infoComponent = infoComponent.append(endLine); + + player.sendMessage(infoComponent); + return true; + } + + // ========== KICK ========== + case "kick" -> { + if (!clans.isLeader(playerUUID) && !clans.isVice(playerUUID)) { + player.sendMessage(miniMessage().deserialize("You are not authorized to kick players.")); + return true; + } + if (args.length < 2) { + player.sendMessage(miniMessage().deserialize("Usage: /clan kick ")); + return true; + } + Clan currentClan = clans.getClanByMember(playerUUID); + if (currentClan == null) { + player.sendMessage(miniMessage().deserialize("Could not find your clan.")); + return true; + } + String targetName = args[1]; + OfflinePlayer targetOffline = Bukkit.getOfflinePlayer(targetName); + + if (targetOffline.getName() == null) { + player.sendMessage(miniMessage().deserialize("No such player found.")); + return true; + } + UUID targetUUID = targetOffline.getUniqueId(); + + // Check: Ist der Spieler im Clan? + if (!currentClan.isMember(targetUUID)) { + player.sendMessage(miniMessage().deserialize(String.format("%s is not in your clan!", targetName))); + return true; + } + // Vice kann keinen Leader kicken + if (clans.isVice(playerUUID) && currentClan.isLeader(targetUUID)) { + player.sendMessage(miniMessage().deserialize("You cannot kick the clan leader!")); + return true; + } + + // Vice entfernen, falls der Gekickte Vice war + if (currentClan.isVice(targetUUID)) { + currentClan.setViceUUID(null); + } + currentClan.getMembers().remove(targetUUID); + currentClan.save(); + + player.sendMessage(miniMessage().deserialize(String.format("You kicked %s from the clan.", targetName))); + if (targetOffline.isOnline()) { + Player targetOnline = (Player) targetOffline; + targetOnline.sendMessage(miniMessage().deserialize(String.format("You have been kicked from the clan %s.", currentClan.getName()))); + } + return true; + } + + // ========== TRANSFER ========== + case "transfer" -> { + if (!clans.isLeader(playerUUID)) { + player.sendMessage(miniMessage().deserialize("Only the clan leader can transfer leadership.")); + return true; + } + if (args.length < 2) { + player.sendMessage(miniMessage().deserialize("Usage: /clan transfer ")); + return true; + } + Clan currentClan = clans.getClanByMember(playerUUID); + if (currentClan == null) { + player.sendMessage(miniMessage().deserialize("Could not find your clan.")); + return true; + } + String newLeaderName = args[1]; + OfflinePlayer newLeaderOffline = Bukkit.getOfflinePlayer(newLeaderName); + if (newLeaderOffline.getName() == null) { + player.sendMessage(miniMessage().deserialize("No such player found.")); + return true; + } + UUID newLeaderUUID = newLeaderOffline.getUniqueId(); + + if (!currentClan.isMember(newLeaderUUID)) { + player.sendMessage(miniMessage().deserialize(String.format("%s is not in your clan!", newLeaderName))); + return true; + } + + // Füge den alten Leader zur Members-Liste hinzu (falls nicht enthalten) + if (!currentClan.getMembers().contains(playerUUID)) { + currentClan.getMembers().add(playerUUID); + } + // Setze neuen Leader + currentClan.setLeaderUUID(newLeaderUUID); + // Falls Vice, entfernen + if (currentClan.isVice(newLeaderUUID)) { + currentClan.setViceUUID(null); + } + currentClan.save(); + + player.sendMessage(miniMessage().deserialize(String.format("You transferred leadership to %s.", newLeaderName))); + if (newLeaderOffline.isOnline()) { + ((Player) newLeaderOffline).sendMessage(miniMessage().deserialize( + String.format("You are now the leader of the clan %s.", currentClan.getName()))); + } + return true; + } + + // ========== PROMOTE ========== + case "promote" -> { + if (!clans.isLeader(playerUUID)) { + player.sendMessage(miniMessage().deserialize("Only the clan leader can promote a member.")); + return true; + } + if (args.length < 2) { + player.sendMessage(miniMessage().deserialize("Usage: /clan promote ")); + return true; + } + Clan currentClan = clans.getClanByMember(playerUUID); + if (currentClan == null) { + player.sendMessage(miniMessage().deserialize("Could not find your clan.")); + return true; + } + String promoteName = args[1]; + OfflinePlayer promoteOffline = Bukkit.getOfflinePlayer(promoteName); + + if (promoteOffline.getName() == null) { + player.sendMessage(miniMessage().deserialize("No such player found.")); + return true; + } + UUID promoteUUID = promoteOffline.getUniqueId(); + + if (!currentClan.isMember(promoteUUID)) { + player.sendMessage(miniMessage().deserialize(String.format("%s is not in your clan!", promoteName))); + return true; + } + // Vice setzen + currentClan.setViceUUID(promoteUUID); + currentClan.save(); + + player.sendMessage(miniMessage().deserialize(String.format("You promoted %s to vice leader.", promoteName))); + if (promoteOffline.isOnline()) { + ((Player) promoteOffline).sendMessage(miniMessage().deserialize( + String.format("You have been promoted to vice leader of clan %s.", currentClan.getName()))); + } + return true; + } + + // ========== DEMOTE ========== + case "demote" -> { + if (!clans.isLeader(playerUUID) && !clans.isVice(playerUUID)) { + player.sendMessage(miniMessage().deserialize("You are not authorized to demote anyone.")); + return true; + } + if (args.length < 2) { + player.sendMessage(miniMessage().deserialize("Usage: /clan demote ")); + return true; + } + Clan currentClan = clans.getClanByMember(playerUUID); + if (currentClan == null) { + player.sendMessage(miniMessage().deserialize("Could not find your clan.")); + return true; + } + String demoteName = args[1]; + OfflinePlayer demoteOffline = Bukkit.getOfflinePlayer(demoteName); + + if (demoteOffline.getName() == null) { + player.sendMessage(miniMessage().deserialize("No such player found.")); + return true; + } + UUID demoteUUID = demoteOffline.getUniqueId(); + + // Check, ob der Spieler Vice ist + if (!currentClan.isVice(demoteUUID)) { + player.sendMessage(miniMessage().deserialize(String.format("%s is not the vice leader!", demoteName))); + return true; + } + // Vice kann nur sich selbst demoten (oder Leader kann Vice demoten) + if (!clans.isLeader(playerUUID) && !playerUUID.equals(demoteUUID)) { + player.sendMessage(miniMessage().deserialize("You can only demote yourself!")); + return true; + } + currentClan.setViceUUID(null); + // Sicherstellen, dass der Spieler in der Memberliste bleibt + if (!currentClan.getMembers().contains(demoteUUID)) { + currentClan.getMembers().add(demoteUUID); + } + currentClan.save(); + + player.sendMessage(miniMessage().deserialize(String.format("You demoted %s to a normal member.", demoteName))); + if (demoteOffline.isOnline()) { + ((Player) demoteOffline).sendMessage(miniMessage().deserialize( + String.format("You have been demoted to a normal member of clan %s.", currentClan.getName()))); + } + return true; + } + + // ========== DISBAND ========== + case "disband" -> { + if (!clans.isLeader(playerUUID)) { + player.sendMessage(miniMessage().deserialize("Only the clan leader can disband the clan.")); + return true; + } + Clan currentClan = clans.getClanByMember(playerUUID); + if (currentClan == null) { + player.sendMessage(miniMessage().deserialize("Could not find your clan.")); + return true; + } + + // Benachrichtige alle Mitglieder + for (UUID memberUUID : currentClan.getMembers()) { + Player memberOnline = Bukkit.getPlayer(memberUUID); + if (memberOnline != null && memberOnline.isOnline()) { + memberOnline.sendMessage(miniMessage().deserialize( + String.format("Your clan %s has been disbanded by the leader.", currentClan.getName()))); + } + } + // Clan entfernen + clans.removeClan(currentClan); + player.sendMessage(miniMessage().deserialize("You have disbanded your clan.")); + return true; + } + + // ========== LEAVE ========== + case "leave" -> { + if (!clans.isInClan(playerUUID)) { + player.sendMessage(miniMessage().deserialize("You are not in a clan!")); + return true; + } + Clan currentClan = clans.getClanByMember(playerUUID); + if (currentClan == null) { + player.sendMessage(miniMessage().deserialize("Could not find your clan.")); + return true; + } + + // Leader kann nicht einfach gehen, ohne disband oder transfer + if (currentClan.isLeader(playerUUID)) { + if (!currentClan.getMembers().isEmpty()) { + player.sendMessage(miniMessage().deserialize("You must transfer leadership or disband the clan before leaving.")); + } else { + // Keine weiteren Mitglieder -> disband + clans.removeClan(currentClan); + player.sendMessage(miniMessage().deserialize( + "You have disbanded your clan (no other members) and left.")); + } + return true; + } + // Vice -> Vice-Position freigeben + if (currentClan.isVice(playerUUID)) { + currentClan.setViceUUID(null); + } + currentClan.getMembers().remove(playerUUID); + currentClan.save(); + + player.sendMessage(miniMessage().deserialize( + String.format("You have left the clan %s.", currentClan.getName()))); + return true; + } + + // ========== MODIFY (Name/Tag) ========== + case "modify" -> { + if (!clans.isLeader(playerUUID)) { + player.sendMessage(miniMessage().deserialize("Only the clan leader can modify clan settings.")); + return true; + } + if (args.length < 3) { + player.sendMessage(miniMessage().deserialize("Usage: /clan modify ")); + return true; + } + String whatToModify = args[1].toLowerCase(); + String newValue = args[2]; + + Clan currentClan = clans.getClanByMember(playerUUID); + if (currentClan == null) { + player.sendMessage(miniMessage().deserialize("Could not find your clan.")); + return true; + } + + switch (whatToModify) { + case "name" -> { + currentClan.setName(newValue); + currentClan.save(); + player.sendMessage(miniMessage().deserialize(String.format("Clan name changed to %s.", newValue))); + } + case "tag" -> { + Component newTag = miniMessage().deserialize(newValue); + currentClan.setTag(newTag); + currentClan.save(); + player.sendMessage(miniMessage().deserialize(String.format("Clan tag changed to %s.", newValue))); + } + default -> { + player.sendMessage(miniMessage().deserialize("You can only modify 'name' or 'tag'.")); + } + } + return true; + } + + // ========== Fallback für Unbekanntes ========== + default -> { + player.sendMessage(miniMessage().deserialize("Unknown subcommand. Use /clan for help.")); + return true; + } + } + } + + /** + * Zeigt eine Liste aller Clans auf dem Server an. + */ + private void sendClanList(Player player) { + // Hier nehmen wir an, dass 'clans.getClans()' alle existierenden Clans liefert. + Collection allClans = clans.getClans(); + if (allClans.isEmpty()) { + player.sendMessage(miniMessage().deserialize("No clans found!")); + return; + } + + Component clanListComponent = Component.empty(); + clanListComponent = clanListComponent.append( + miniMessage().deserialize("=== List of clans ===\n")); + + for (Clan clan : allClans) { + Component clanComponent = miniMessage().deserialize( + String.format(" - %s\n", clan.getName())); + clanListComponent = clanListComponent.append(clanComponent); + } + clanListComponent = clanListComponent.append( + miniMessage().deserialize("=====================")); + + player.sendMessage(clanListComponent); + } + + /** + * Hilfsfunktion: Zeigt eine übersichtliche Hilfe basierend auf der Rolle (Leader, Vice, Member, Kein-Mitglied). + */ + private void sendHelpMessage(Player player, UUID playerUUID) { + if (clans.isLeader(playerUUID)) { + Component l1 = miniMessage().deserialize("=== Clan Commands ===\n"); + Component l2 = miniMessage().deserialize(" - /clan info\n"); + Component l3 = miniMessage().deserialize(" - /clan invite\n"); + Component l4 = miniMessage().deserialize(" - /clan kick\n"); + Component l5 = miniMessage().deserialize(" - /clan transfer\n"); + Component l6 = miniMessage().deserialize(" - /clan promote\n"); + Component l7 = miniMessage().deserialize(" - /clan demote\n"); + Component l8 = miniMessage().deserialize(" - /clan disband\n"); + Component l9 = miniMessage().deserialize(" - /clan leave\n"); + Component l10 = miniMessage().deserialize(" - /clan accept\n"); + Component l11 = miniMessage().deserialize(" - /clan deny\n"); + Component l12 = miniMessage().deserialize(" - /clan modify\n"); + Component l13 = miniMessage().deserialize(" - /clan list\n"); + Component l14 = miniMessage().deserialize("====================="); + + player.sendMessage(l1.append(l2).append(l3).append(l4).append(l5) + .append(l6).append(l7).append(l8).append(l9).append(l10).append(l11).append(l12).append(l13).append(l14)); + } else if (clans.isVice(playerUUID)) { + Component l1 = miniMessage().deserialize("=== Clan Commands ===\n"); + Component l2 = miniMessage().deserialize(" - /clan info\n"); + Component l3 = miniMessage().deserialize(" - /clan invite\n"); + Component l4 = miniMessage().deserialize(" - /clan kick\n"); + Component l5 = miniMessage().deserialize(" - /clan demote\n"); + Component l6 = miniMessage().deserialize(" - /clan leave\n"); + Component l7 = miniMessage().deserialize(" - /clan accept\n"); + Component l8 = miniMessage().deserialize(" - /clan deny\n"); + Component l9 = miniMessage().deserialize(" - /clan list\n"); + Component l10 = miniMessage().deserialize("====================="); + + player.sendMessage(l1.append(l2).append(l3).append(l4).append(l5) + .append(l6).append(l7).append(l8).append(l9).append(l10)); + } else if (clans.isMember(playerUUID)) { + Component l1 = miniMessage().deserialize("=== Clan Commands ===\n"); + Component l2 = miniMessage().deserialize(" - /clan info\n"); + Component l3 = miniMessage().deserialize(" - /clan leave\n"); + Component l4 = miniMessage().deserialize(" - /clan list\n"); + Component l5 = miniMessage().deserialize("====================="); + + player.sendMessage(l1.append(l2).append(l3).append(l4).append(l5)); + } else { + // Spieler ist in keinem Clan + Component l1 = miniMessage().deserialize("=== Clan Commands ===\n"); + Component l2 = miniMessage().deserialize(" - /clan create\n"); + Component l3 = miniMessage().deserialize(" - /clan join\n"); + Component l4 = miniMessage().deserialize(" - /clan accept\n"); + Component l5 = miniMessage().deserialize(" - /clan deny\n"); + Component l6 = miniMessage().deserialize(" - /clan list\n"); + Component l7 = miniMessage().deserialize("====================="); + + player.sendMessage(l1.append(l2).append(l3).append(l4).append(l5).append(l6).append(l7)); + } } @Override - public @Nullable List onTabComplete(@NotNull CommandSender sender, @NotNull Command cmd, @NotNull String list, @NotNull String[] args) { - Player player = (Player) sender; + public @Nullable List onTabComplete(@NotNull CommandSender sender, + @NotNull Command cmd, + @NotNull String label, + @NotNull String[] args) { + if (!(sender instanceof Player player)) return List.of(); UUID playerUUID = player.getUniqueId(); + + // Erste Ebene der Subcommands if (args.length == 1) { if (clans.isLeader(playerUUID)) { - return Stream.of("info", "invite", "kick", "transfer", "promote", "demote", "disband", "leave", "accept", "deny", "modify") + return Stream.of("info", "invite", "kick", "transfer", "promote", + "demote", "disband", "leave", "accept", "deny", + "modify", "list") .filter(s -> s.startsWith(args[0])) - .toList(); + .collect(Collectors.toList()); } else if (clans.isVice(playerUUID)) { - return Stream.of("info", "invite", "kick", "demote", "leave", "accept", "deny") + return Stream.of("info", "invite", "kick", "demote", + "leave", "accept", "deny", "list") .filter(s -> s.startsWith(args[0])) - .toList(); + .collect(Collectors.toList()); } else if (clans.isMember(playerUUID)) { - return Stream.of("info","leave") + return Stream.of("info", "leave", "list") .filter(s -> s.startsWith(args[0])) - .toList(); + .collect(Collectors.toList()); } else { - return Stream.of("create", "join", "accept", "deny") + return Stream.of("create", "join", "accept", "deny", "list") .filter(s -> s.startsWith(args[0])) - .toList(); + .collect(Collectors.toList()); } - } else if (args.length == 2) { + } + + // Zweite Ebene + if (args.length == 2) { if (clans.isLeader(playerUUID)) { - if (args[0].equalsIgnoreCase("invite")) { - return Bukkit.getOnlinePlayers().stream() - .filter(p -> !clans.isMember(p.getUniqueId())) - .map(Player::getName) - .filter(name -> name.startsWith(args[1])) - .collect(Collectors.toList()); - } else if (args[0].equalsIgnoreCase("kick")) { - return clans.getClanByMember(playerUUID).getMembers().stream() - .map(UUID::toString) - .filter(s -> s.startsWith(args[1])) - .collect(Collectors.toList()); - } else if (args[0].equalsIgnoreCase("promote")) { - return clans.getClanByMember(playerUUID).getMembers().stream() - .map(UUID::toString) - .filter(s -> s.startsWith(args[1])) - .collect(Collectors.toList()); - } else if (args[0].equalsIgnoreCase("demote")) { - return Collections.singletonList(plugin.getServer().getOfflinePlayer(clans.getClanByMember(playerUUID).getViceUUID()).getName()); - } else if (args[0].equalsIgnoreCase("accept")) { - List joins = getClanJoinRequests(args, playerUUID); - if (joins != null) return joins; - } else if (args[0].equalsIgnoreCase("deny")) { - List joins = getClanJoinRequests(args, playerUUID); - if (joins != null) return joins; - } else if (args[0].equalsIgnoreCase("modify")) { - return Stream.of("name", "tag") - .filter(s -> s.startsWith(args[1])) - .toList(); + switch (args[0].toLowerCase()) { + case "invite" -> { + return Bukkit.getOnlinePlayers().stream() + .filter(p -> !clans.isMember(p.getUniqueId())) + .map(Player::getName) + .filter(name -> name.startsWith(args[1])) + .collect(Collectors.toList()); + } + case "kick", "promote" -> { + Clan clan = clans.getClanByMember(playerUUID); + if (clan == null) return List.of(); + return clan.getMembers().stream() + .map(uuid -> Bukkit.getOfflinePlayer(uuid).getName()) + .filter(Objects::nonNull) + .filter(name -> name.startsWith(args[1])) + .collect(Collectors.toList()); + } + case "demote" -> { + Clan clan = clans.getClanByMember(playerUUID); + if (clan == null || clan.getViceUUID() == null) return List.of(); + String viceName = Optional.ofNullable( + Bukkit.getOfflinePlayer(clan.getViceUUID()).getName()) + .orElse(clan.getViceUUID().toString()); + return viceName.startsWith(args[1]) + ? List.of(viceName) + : List.of(); + } + case "accept", "deny" -> { + List joins = getClanJoinRequests(args, playerUUID); + if (joins != null) return joins; + } + case "modify" -> { + return Stream.of("name", "tag") + .filter(s -> s.startsWith(args[1])) + .collect(Collectors.toList()); + } + default -> {} } } else if (clans.isVice(playerUUID)) { - if (args[0].equalsIgnoreCase("invite")) { - return Bukkit.getOnlinePlayers().stream() - .filter(p -> !clans.isMember(p.getUniqueId())) - .map(Player::getName) - .filter(name -> name.startsWith(args[1])) - .collect(Collectors.toList()); - } else if (args[0].equalsIgnoreCase("kick")) { - return clans.getClanByMember(playerUUID).getMembers().stream() - .map(UUID::toString) - .filter(s -> s.startsWith(args[1])) - .collect(Collectors.toList()); - } else if (args[0].equalsIgnoreCase("accept")) { - List joins = getClanJoinRequests(args, playerUUID); - if (joins != null) return joins; - } else if (args[0].equalsIgnoreCase("deny")) { - List joins = getClanJoinRequests(args, playerUUID); - if (joins != null) return joins; - } else if (args[0].equalsIgnoreCase("demote")) { - return Collections.singletonList(plugin.getServer().getOfflinePlayer(clans.getClanByMember(playerUUID).getViceUUID()).getName()); + switch (args[0].toLowerCase()) { + case "invite" -> { + return Bukkit.getOnlinePlayers().stream() + .filter(p -> !clans.isMember(p.getUniqueId())) + .map(Player::getName) + .filter(name -> name.startsWith(args[1])) + .collect(Collectors.toList()); + } + case "kick" -> { + Clan clan = clans.getClanByMember(playerUUID); + if (clan == null) return List.of(); + return clan.getMembers().stream() + .map(uuid -> Bukkit.getOfflinePlayer(uuid).getName()) + .filter(Objects::nonNull) + .filter(name -> name.startsWith(args[1])) + .collect(Collectors.toList()); + } + case "demote" -> { + Clan clan = clans.getClanByMember(playerUUID); + if (clan == null || clan.getViceUUID() == null) return List.of(); + String viceName = Optional.ofNullable( + Bukkit.getOfflinePlayer(clan.getViceUUID()).getName()) + .orElse(clan.getViceUUID().toString()); + return viceName.startsWith(args[1]) + ? List.of(viceName) + : List.of(); + } + case "accept", "deny" -> { + List joins = getClanJoinRequests(args, playerUUID); + if (joins != null) return joins; + } + default -> {} } } else { - if (args[0].equalsIgnoreCase("accept")) { - return clanInvites.entrySet().stream() - .filter(entry -> entry.getValue().contains(playerUUID)) - .map(entry -> entry.getKey().getName()) - .collect(Collectors.toList()); - } else if (args[0].equalsIgnoreCase("deny")) { - return clanInvites.entrySet().stream() - .filter(entry -> entry.getValue().contains(playerUUID)) - .map(entry -> entry.getKey().getName()) - .collect(Collectors.toList()); - } else if (args[0].equalsIgnoreCase("join")) { - return clans.getClans().stream() - .map(Clan::getName) - .filter(s -> s.startsWith(args[1])) - .collect(Collectors.toList()); - } else if (args[0].equalsIgnoreCase("create")) { - return Collections.singletonList(""); + switch (args[0].toLowerCase()) { + case "accept", "deny" -> { + // Zeige alle Clan-Einladungen an + return clanInvites.entrySet().stream() + .filter(entry -> entry.getValue().contains(playerUUID)) + .map(entry -> entry.getKey().getName()) + .filter(name -> name.startsWith(args[1])) + .collect(Collectors.toList()); + } + case "join" -> { + // Zeige alle Clans an + return clans.getClans().stream() + .map(Clan::getName) + .filter(s -> s.startsWith(args[1])) + .collect(Collectors.toList()); + } + case "create" -> { + return Collections.singletonList(""); + } + default -> {} } } - } else if (args.length == 3) { + } + + // Dritte Ebene (z. B. /clan modify name ) + if (args.length == 3) { if (clans.isLeader(playerUUID)) { - if (args[1].equalsIgnoreCase("name")) { - return Collections.singletonList(""); - } else if (args[1].equalsIgnoreCase("tag")) { - return Collections.singletonList(""); - } - } else { - if (args[0].equalsIgnoreCase("create")) { - return Collections.singletonList(""); + if (args[0].equalsIgnoreCase("modify")) { + if (args[1].equalsIgnoreCase("name")) { + return Collections.singletonList(""); + } else if (args[1].equalsIgnoreCase("tag")) { + return Collections.singletonList(""); + } } + } else if (args[0].equalsIgnoreCase("create")) { + return Collections.singletonList(""); } } return List.of(); } + /** + * Liefert zu einem Namen die passende UUID aus einer UUID-Liste (z. B. Join-Anfragen). + */ @Nullable - private List getClanJoinRequests(@NotNull String[] args, UUID playerUUID) { - LinkedList joins = clanJoins.get(clans.getClanByMember(playerUUID)); - if (joins != null) { - return joins.stream() - .map(uuid -> plugin.getServer().getOfflinePlayer(uuid).getName()) - .filter(Objects::nonNull) - .filter(s -> s.startsWith(args[1])) - .collect(Collectors.toList()); + private UUID getUuidByName(List uuidList, String playerName) { + for (UUID uuid : uuidList) { + OfflinePlayer off = Bukkit.getOfflinePlayer(uuid); + if (off.getName() != null && off.getName().equalsIgnoreCase(playerName)) { + return uuid; + } } return null; } + + /** + * Hilfsfunktion für Tab-Completion (Zeigt Spielernamen von Join-Anfragen). + */ + @Nullable + private List getClanJoinRequests(@NotNull String[] args, UUID playerUUID) { + Clan clan = clans.getClanByMember(playerUUID); + if (clan == null) return null; + + LinkedList joins = clanJoins.get(clan); + if (joins == null) return null; + + return joins.stream() + .map(uuid -> Bukkit.getOfflinePlayer(uuid).getName()) + .filter(Objects::nonNull) + .filter(name -> name.startsWith(args[1])) + .collect(Collectors.toList()); + } }