diff --git a/README.md b/README.md index 98a65cb1..7ff45773 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@

An easy to use toolset for Build Teams in the BuildTheEarth project.
- WIKI » + WIKI »

@@ -69,15 +69,15 @@ The **\*** symbol shows that this feature is still being developed or under main 🚩 **How to install:** 1. Download BuildTeamTools [here](https://www.spigotmc.org/resources/buildteamtools.101854/). 2. Place the downloaded plugin in your server's **../plugins** folder. -3. *(Optional)* Some of the modules require intial configuration or dependencies to run: +3. *(Optional)* Some of the modules require initial configuration or dependencies to run: > - To activate the **Navigation Module**, please configure an **API Key** in the config.yml file. > - To activate the **Network Module**, please configure an **API Key** in the config.yml file. -> - To activate the **Generator Module**, please install the [WorldEdit](https://dev.bukkit.org/projects/worldedit) or [AsyncWorldEdit](https://www.spigotmc.org/resources/asyncworldedit.327/) plugin. +> - To activate the **Generator Module**, please install the [FastAsyncWorldEdit](https://modrinth.com/plugin/fastasyncworldedit/versions/) plugin. > - To activate the **Tree Generator Component**, install the [SchematicBrush 0.5.2](https://dev.bukkit.org/projects/schematicbrush) plugin. 4. Restart your server. 5. Done! -**For more help, please see the [wiki](https://github.com/BuildTheEarth/BuildTeamTools/wiki/Installation).** +**For more help, please see the [wiki](https://resources.buildtheearth.net/s/btt).** ## Contributors Thank you to the following developers for contributing towards the plugin: @@ -88,20 +88,21 @@ Thank you to the following developers for contributing towards the plugin: - **v4siv** - **Adam** - **Noah Husby** +- **Zoriot** ## Index -Click the links below for more information, or see the [wiki](https://github.com/BuildTheEarth/BuildTeamTools/wiki). - -- [Generators](https://github.com/BuildTheEarth/BuildTeamTools/wiki/Generators) - - [Houses](https://github.com/BuildTheEarth/BuildTeamTools/wiki/House-Command) - - [Roads](https://github.com/BuildTheEarth/BuildTeamTools/wiki/Road-Command) - - [Railways](https://github.com/BuildTheEarth/BuildTeamTools/wiki/Rail-Command) - - [Trees](https://github.com/BuildTheEarth/BuildTeamTools/wiki/Tree-Command) - - [Fields](https://github.com/BuildTheEarth/BuildTeamTools/wiki/Field-Command) -- [Sledgehammer](https://github.com/noahhusby/Sledgehammer) (PLANNED) -- Statistics (PLANNED) -- Plot System (PLANNED) -- Tutorials (PLANNED) +Click the links below for more information, or see the [wiki](https://resources.buildtheearth.net/s/btt). + +- [Generators](https://resources.buildtheearth.net/s/btt/doc/generator-module-13zqgI4yFA) + - [Houses](https://resources.buildtheearth.net/s/btt/doc/house-generator-YKQunon6Bp) + - [Roads](https://resources.buildtheearth.net/s/btt/doc/road-generator-QqKBBP0nqO) + - [Railways](https://resources.buildtheearth.net/s/btt/doc/rail-generator-EfgKXdBvk1) + - [Trees](https://resources.buildtheearth.net/s/btt/doc/tree-generator-pnDmYC9hzW) + - [Fields](https://resources.buildtheearth.net/s/btt/doc/field-generator-OqIN2BrZT7) +- [Sledgehammer](https://resources.buildtheearth.net/s/btt/doc/sledgehammer-module-T7I0PWPsTD) +- [Statistics](https://resources.buildtheearth.net/s/btt/doc/statistics-module-iM7IfoKroF) (PLANNED) +- [Plot System](https://resources.buildtheearth.net/s/btt/doc/plot-system-module-kkcH00jpcQ) (PLANNED) +- [Tutorials](https://resources.buildtheearth.net/s/btt/doc/tutorials-module-lkc3LaOrql) (PLANNED) - Miscellaneous (PLANNED) -- Security (PLANNED) +- [Security](https://resources.buildtheearth.net/s/btt/doc/security-module-iOmxV0djrW) (PLANNED) diff --git a/pom.xml b/pom.xml index 159b0981..7314100c 100644 --- a/pom.xml +++ b/pom.xml @@ -280,7 +280,7 @@ com.github.cryptomorin XSeries - 13.3.3 + 13.4.0 compile @@ -289,7 +289,7 @@ net.wesjd anvilgui - 1.10.7-SNAPSHOT + 1.10.8-SNAPSHOT compile diff --git a/src/main/java/net/buildtheearth/BuildTeamTools.java b/src/main/java/net/buildtheearth/BuildTeamTools.java index 967943d2..b9bcd4bf 100644 --- a/src/main/java/net/buildtheearth/BuildTeamTools.java +++ b/src/main/java/net/buildtheearth/BuildTeamTools.java @@ -52,7 +52,7 @@ public void onEnable() { StatsModule.getInstance(), MiscModule.getInstance() ); - ModuleHandler.getInstance().enableAll(null, true); + ModuleHandler.getInstance().enableAll(null); } @Override diff --git a/src/main/java/net/buildtheearth/modules/ModuleHandler.java b/src/main/java/net/buildtheearth/modules/ModuleHandler.java index c006ef16..22253769 100644 --- a/src/main/java/net/buildtheearth/modules/ModuleHandler.java +++ b/src/main/java/net/buildtheearth/modules/ModuleHandler.java @@ -5,7 +5,8 @@ import net.buildtheearth.utils.ChatHelper; import org.bukkit.Bukkit; import org.bukkit.ChatColor; -import org.bukkit.entity.Player; +import org.bukkit.command.CommandSender; +import org.jetbrains.annotations.NotNull; import javax.annotation.Nullable; import java.util.ArrayList; @@ -50,11 +51,10 @@ public void registerModules(Module... modules) { * Enables a specific module * * @param module {@link Module} - * @param executor the player that executed the command. If null, the command was executed by the system. - * @param isStarting if the server is starting + * @param executor the sender, if it's triggered via command, else null, when it's called by btt on plugin start. * @return True if successfully enabled, false if not */ - public boolean enable(Module module, Player executor, boolean isStarting) { + public boolean enable(@NotNull Module module, @Nullable CommandSender executor) { for (Module m : modules) if (m.getModuleName().equals(module.getModuleName()) && m.isEnabled()) return false; @@ -71,14 +71,14 @@ public boolean enable(Module module, Player executor, boolean isStarting) { module.enable(); } catch (Exception ex) { if (BuildTeamTools.getInstance().isDebug()) { - ChatHelper.logError("An error occurred while enabling the %s Module: %s", module.getModuleName(), ex.getMessage()); - ex.printStackTrace(); + ChatHelper.logError("An error occurred while enabling the %s Module: %s", ex, module.getModuleName(), + ex.getMessage()); } module.shutdown(ex.getMessage()); } - if (!isStarting) { + if (executor != null) { if (module.isEnabled() && BuildTeamTools.getInstance().isDebug()) ChatHelper.log("Successfully enabled %s Module", module.getModuleName()); else { @@ -112,10 +112,10 @@ public boolean enable(Module module, Player executor, boolean isStarting) { * Disables a specific module * * @param module {@link Module} - * @param executor the player that executed the command. If null, the command was executed by the system. + * @param executor the sender, if it's triggered via command, else null - when it's called by btt on plugin stop). * @return True if successfully disabled, false if not */ - public boolean disable(Module module, Player executor) { + public boolean disable(@NotNull Module module, @Nullable CommandSender executor) { boolean contains = false; for(Module m : modules) if (m.getModuleName().equals(module.getModuleName())) { @@ -131,7 +131,7 @@ public boolean disable(Module module, Player executor) { if (!module.isEnabled()) { if(BuildTeamTools.getInstance().isDebug()) ChatHelper.log("Successfully disabled %s Module", module.getModuleName()); - }else { + } else { String reason = ""; if(module.getError() != null && !module.getError().isEmpty()) @@ -158,23 +158,22 @@ public boolean disable(Module module, Player executor) { /** Enables all modules * - * @param executor the player that executed the command. If null, the command was executed by the system. - * @param isStarting if the server is starting + * @param executor the player, if it's triggered via command, else null (when it's called by btt on plugin start). */ - public void enableAll(@Nullable Player executor, boolean isStarting) { + public void enableAll(@Nullable CommandSender executor) { for (Module module : new ArrayList<>(modules)) if (!module.isEnabled()) - enable(module, executor, isStarting); + enable(module, executor); - if(isStarting) + if (executor == null) sendBuildTeamToolsConsoleStartupMessage(); } /** Disables all modules * - * @param executor the player that executed the command. If null, the command was executed by the system. + * @param executor the player, if it's triggered via command, else null (when it's called by btt on plugin stop). */ - public void disableAll(@Nullable Player executor) { + public void disableAll(@Nullable CommandSender executor) { for (Module module : new ArrayList<>(modules)) if (module.isEnabled()) disable(module, executor); @@ -183,9 +182,9 @@ public void disableAll(@Nullable Player executor) { /** * Reloads all modules */ - public void reloadAll(Player executor) { + public void reloadAll(@Nullable CommandSender executor) { disableAll(executor); - enableAll(executor, false); + enableAll(executor); } private void sendBuildTeamToolsConsoleStartupMessage(){ diff --git a/src/main/java/net/buildtheearth/modules/common/CommonModule.java b/src/main/java/net/buildtheearth/modules/common/CommonModule.java index dd299487..b9e67f6c 100644 --- a/src/main/java/net/buildtheearth/modules/common/CommonModule.java +++ b/src/main/java/net/buildtheearth/modules/common/CommonModule.java @@ -81,7 +81,7 @@ public void enable() { @Override public void registerCommands() { - registerCommand("buildteam", new BuildTeamToolsCommand()); + registerCommand("buildteamtools", new BuildTeamToolsCommand()); } @Override diff --git a/src/main/java/net/buildtheearth/modules/common/commands/BuildTeamToolsCommand.java b/src/main/java/net/buildtheearth/modules/common/commands/BuildTeamToolsCommand.java index 5ba00ccb..50131c53 100644 --- a/src/main/java/net/buildtheearth/modules/common/commands/BuildTeamToolsCommand.java +++ b/src/main/java/net/buildtheearth/modules/common/commands/BuildTeamToolsCommand.java @@ -1,5 +1,6 @@ package net.buildtheearth.modules.common.commands; +import com.google.gson.Gson; import net.buildtheearth.BuildTeamTools; import net.buildtheearth.modules.Module; import net.buildtheearth.modules.ModuleHandler; @@ -17,9 +18,9 @@ import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandSender; import org.bukkit.command.TabCompleter; -import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -27,20 +28,13 @@ public class BuildTeamToolsCommand implements CommandExecutor, TabCompleter { - public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) { + public boolean onCommand(@NotNull CommandSender sender, @NotNull Command cmd, @NotNull String label, String @NotNull [] args) { - if(!(sender instanceof Player)){ - sender.sendMessage("§cYou need to be a player to execute this command."); - return true; - } - Player player = (Player) sender; - - if(!player.hasPermission(Permissions.BUILD_TEAM_TOOLS)){ + if(!sender.hasPermission(Permissions.BUILD_TEAM_TOOLS)){ sender.sendMessage("§cYou don't have permission to execute this command."); return true; } - if(args.length == 0){ sendBuildTeamToolsInfo(sender); return true; @@ -58,9 +52,8 @@ public boolean onCommand(CommandSender sender, Command cmd, String label, String return true; } - if(args[0].equalsIgnoreCase("communicators")) { - if(!player.hasPermission(Permissions.BUILD_TEAM_TOOLS_COMMUNICATORS)){ + if(!sender.hasPermission(Permissions.BUILD_TEAM_TOOLS_COMMUNICATORS)){ sender.sendMessage("§cYou don't have permission to execute this command."); return true; } @@ -73,7 +66,7 @@ public boolean onCommand(CommandSender sender, Command cmd, String label, String } if (args[0].equalsIgnoreCase("cache")) { - if(!player.hasPermission(Permissions.BUILD_TEAM_TOOLS_CACHE)){ + if(!sender.hasPermission(Permissions.BUILD_TEAM_TOOLS_CACHE)){ sender.sendMessage("§cYou don't have permission to execute this command."); return true; } @@ -90,15 +83,13 @@ public boolean onCommand(CommandSender sender, Command cmd, String label, String return true; } - - if (args[0].equalsIgnoreCase("debug")) { - if(!player.hasPermission(Permissions.BUILD_TEAM_TOOLS_DEBUG)){ + if(!sender.hasPermission(Permissions.BUILD_TEAM_TOOLS_DEBUG)){ sender.sendMessage("§cYou don't have permission to execute this command."); return true; } - if(!(args.length > 1)) { + if(args.length <= 1) { sender.sendMessage("§cYou need to add a value: true/false"); return true; } @@ -111,12 +102,12 @@ public boolean onCommand(CommandSender sender, Command cmd, String label, String boolean debug = Boolean.parseBoolean(args[1]); BuildTeamTools.getInstance().setDebug(debug); - player.sendMessage(ChatHelper.getStandardString("§7Debug Mode was set to: %s", debug)); + sender.sendMessage(ChatHelper.getStandardString("§7Debug Mode was set to: %s", debug)); return true; } if (args[0].equalsIgnoreCase("checkForUpdates")) { - if(!player.hasPermission(Permissions.BUILD_TEAM_TOOLS_CHECK_FOR_UPDATES)){ + if(!sender.hasPermission(Permissions.BUILD_TEAM_TOOLS_CHECK_FOR_UPDATES)){ sender.sendMessage("§cYou don't have permission to execute this command."); return true; } @@ -131,23 +122,21 @@ public boolean onCommand(CommandSender sender, Command cmd, String label, String } if(args[0].equalsIgnoreCase("reload")) { - if(!player.hasPermission(Permissions.BUILD_TEAM_TOOLS_RELOAD)){ + if(!sender.hasPermission(Permissions.BUILD_TEAM_TOOLS_RELOAD)){ sender.sendMessage("§cYou don't have permission to execute this command."); return true; } sender.sendMessage(ChatHelper.getStandardString("§7Reloading all modules...")); - ModuleHandler.getInstance().reloadAll(player); + ModuleHandler.getInstance().reloadAll(sender); sender.sendMessage(ChatHelper.getStandardString("§7All modules have been reloaded.")); } - - return true; } @Override - public List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String[] args) { + public List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String @NotNull [] args) { if(args.length == 1) return Arrays.asList("help", "communicators", "checkForUpdates", "cache", "debug", "reload"); @@ -175,11 +164,6 @@ public static void sendBuildTeamToolsInfo(CommandSender sender){ && NetworkModule.getInstance().getBuildTeam().getServerName() != null) serverName = NetworkModule.getInstance().getBuildTeam().getServerName(); - String continent = "-"; - if (NetworkModule.getInstance().getBuildTeam() != null - && NetworkModule.getInstance().getBuildTeam().getContinent() != null) - continent = NetworkModule.getInstance().getBuildTeam().getContinent().getLabel(); - String status = "§c§lDISCONNECTED"; if (NetworkModule.getInstance().getBuildTeam() != null && NetworkModule.getInstance().getBuildTeam().isConnected() && !buildTeamID.equals("-") && !serverName.equals("-")) @@ -189,9 +173,6 @@ else if (!buildTeamID.equals("-") && !serverName.equals("-")) boolean debug = BuildTeamTools.getInstance().isDebug(); - - - sender.sendMessage("§eStatus: " + status); sender.sendMessage("§eVersion: §7" + BuildTeamTools.getInstance().getDescription().getVersion()); @@ -213,14 +194,24 @@ else if(!module.isEnabled()) } if(NetworkModule.getInstance().getBuildTeam() != null){ + + List regions = new ArrayList<>(); + List continents = new ArrayList<>(); + for (Region region : NetworkModule.getInstance().getBuildTeam().getRegions()) { + if (region.getContinent() != null && !continents.contains(region.getContinent().getLabel())) + continents.add(region.getContinent().getLabel()); + + if (!regions.contains(region.getName())) + regions.add(region.getName()); + } + + Gson gson = new Gson(); + sender.sendMessage(""); sender.sendMessage("§eBuildTeam ID: §7" + buildTeamID); sender.sendMessage("§eServer Name: §7" + serverName); - sender.sendMessage("§eContinent: §7" + continent); - sender.sendMessage("§eRegions: §7"); - - for(Region region : NetworkModule.getInstance().getBuildTeam().getRegions()) - sender.sendMessage("- §7" + region.getName()); + sender.sendMessage("§eContinents: §7" + gson.toJson(continents)); + sender.sendMessage("§eRegions: §7" + gson.toJson(regions)); } sender.sendMessage(""); diff --git a/src/main/java/net/buildtheearth/modules/generator/commands/GeneratorCommand.java b/src/main/java/net/buildtheearth/modules/generator/commands/GeneratorCommand.java index 3d6ca681..c24ca9c8 100644 --- a/src/main/java/net/buildtheearth/modules/generator/commands/GeneratorCommand.java +++ b/src/main/java/net/buildtheearth/modules/generator/commands/GeneratorCommand.java @@ -10,11 +10,12 @@ import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; public class GeneratorCommand implements CommandExecutor { - public boolean onCommand(CommandSender sender, Command cmd, String cmdLabel, String[] args) { + public boolean onCommand(@NotNull CommandSender sender, @NotNull Command cmd, @NotNull String cmdLabel, String @NotNull [] args) { if (!(sender instanceof Player)) { sender.sendMessage("§cOnly players can execute this command."); return true; @@ -35,65 +36,53 @@ public boolean onCommand(CommandSender sender, Command cmd, String cmdLabel, Str // Command Usage: /gen house ... - if (args[0].equals("house")) { - GeneratorModule.getInstance().getHouse().analyzeCommand(p, args); - return true; - } - - // Command Usage: /gen road ... - if (args[0].equals("road")) { - GeneratorModule.getInstance().getRoad().analyzeCommand(p, args); - return true; - } - - // Command Usage: /gen rail ... - if (args[0].equals("rail")) { - GeneratorModule.getInstance().getRail().analyzeCommand(p, args); - return true; - } - - // Command Usage: /gen tree ... - if (args[0].equals("tree")) { - GeneratorModule.getInstance().getTree().analyzeCommand(p, args); - return true; - } + switch (args[0]) { + case "house": + GeneratorModule.getInstance().getHouse().analyzeCommand(p, args); + return true; - // Command Usage: /gen field ... - if (args[0].equals("field")) { - GeneratorModule.getInstance().getField().analyzeCommand(p, args); - return true; - } + // Command Usage: /gen road ... + case "road": + GeneratorModule.getInstance().getRoad().analyzeCommand(p, args); + return true; + // Command Usage: /gen rail ... + case "rail": + GeneratorModule.getInstance().getRail().analyzeCommand(p, args); + return true; - // Command Usage: /gen history - if (args[0].equals("history")) { - if (GeneratorModule.getInstance().getPlayerHistory(p).getHistoryEntries().isEmpty()) { - p.sendMessage("§cYou didn't generate any structures yet. Use /gen to create one."); + // Command Usage: /gen tree ... + case "tree": + GeneratorModule.getInstance().getTree().analyzeCommand(p, args); return true; - } - ChatHelper.sendMessageBox(sender, "Generator History for " + p.getName(), () -> { - for (History.HistoryEntry history : GeneratorModule.getInstance().getPlayerHistory(p).getHistoryEntries()) { - long timeDifference = System.currentTimeMillis() - history.getTimeCreated(); + // Command Usage: /gen field ... + case "field": + GeneratorModule.getInstance().getField().analyzeCommand(p, args); + return true; - p.sendMessage("§e- " + history.getGeneratorType().name() + " §7-§e " + Utils.toDate(p, timeDifference) + " ago §7-§e " + history.getWorldEditCommandCount() + " Commands executed"); + // Command Usage: /gen history + case "history": + if (GeneratorModule.getInstance().getPlayerHistory(p).getHistoryEntries().isEmpty()) { + p.sendMessage("§cYou didn't generate any structures yet. Use /gen to create one."); + return true; } - }); - return true; - } - if(args[0].equals("undo")) { - GeneratorModule.getInstance().getPlayerHistory(p).undoCommand(p); - return true; - } - - if(args[0].equals("redo")) { - GeneratorModule.getInstance().getPlayerHistory(p).redoCommand(p); - return true; - } + ChatHelper.sendMessageBox(sender, "Generator History for " + p.getName(), () -> { + for (History.HistoryEntry history : GeneratorModule.getInstance().getPlayerHistory(p).getHistoryEntries()) { + long timeDifference = System.currentTimeMillis() - history.getTimeCreated(); + p.sendMessage("§e- " + history.getGeneratorType().name() + " §7-§e " + Utils.toDate(timeDifference) + " ago §7-§e " + history.getWorldEditCommandCount() + " Commands executed"); + } + }); + return true; - if(args[0].equals("cancel")){ + case "undo": + GeneratorModule.getInstance().getPlayerHistory(p).undoCommand(p); + return true; + case "redo": + GeneratorModule.getInstance().getPlayerHistory(p).redoCommand(p); + return true; } sendHelp(p); diff --git a/src/main/java/net/buildtheearth/modules/navigation/NavUtils.java b/src/main/java/net/buildtheearth/modules/navigation/NavUtils.java new file mode 100644 index 00000000..03864890 --- /dev/null +++ b/src/main/java/net/buildtheearth/modules/navigation/NavUtils.java @@ -0,0 +1,120 @@ +package net.buildtheearth.modules.navigation; + +import com.google.common.io.ByteArrayDataOutput; +import com.google.common.io.ByteStreams; +import lombok.experimental.UtilityClass; +import net.buildtheearth.BuildTeamTools; +import net.buildtheearth.modules.navigation.components.warps.model.WarpGroup; +import net.buildtheearth.modules.network.NetworkModule; +import net.buildtheearth.modules.network.model.BuildTeam; +import net.buildtheearth.utils.ChatHelper; +import net.md_5.bungee.api.chat.ClickEvent; +import net.md_5.bungee.api.chat.ComponentBuilder; +import net.md_5.bungee.api.chat.HoverEvent; +import net.md_5.bungee.api.chat.TextComponent; +import org.bukkit.Bukkit; +import org.bukkit.UnsafeValues; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +@UtilityClass +public class NavUtils { + public static void sendPlayerToConnectedServer(Player player, String server) { + if (NetworkModule.getInstance().getBuildTeam() == null + || !NetworkModule.getInstance().getBuildTeam().isConnected()) + return; + + ByteArrayDataOutput out = ByteStreams.newDataOutput(); + out.writeUTF("Connect"); + out.writeUTF(server); + player.sendPluginMessage(BuildTeamTools.getInstance(), "BungeeCord", out.toByteArray()); + } + + public static boolean isTransferCapable(@NotNull Player player, @NotNull BuildTeam targetBuildTeam) { + // Must be 1.20.5 / 766 or higher for transfer capability + int playerVersion = player.getProtocolVersion(); + UnsafeValues unsafeValues = Bukkit.getUnsafe(); + int serverProtocolVersion = unsafeValues.getProtocolVersion(); + + ChatHelper.logDebug("Transfer check - Player protocol: %d, Server protocol: %s, BuildTeam: %s, Allows transfers: %b, IP: %s", playerVersion, serverProtocolVersion, targetBuildTeam.getBlankName(), targetBuildTeam.isAllowsTransfers(), targetBuildTeam.getIP()); + + return targetBuildTeam.isAllowsTransfers() && playerVersion >= 766 && serverProtocolVersion >= 766; + } + + public static void transferPlayer(@NotNull Player player, @NotNull String ip) { + int sep = ip.indexOf(':'); + String server = sep >= 0 ? ip.substring(0, sep) : ip; + int port = sep >= 0 ? Integer.parseInt(ip.substring(sep + 1)) : 25565; + player.transfer(server, port); + } + + /** + * Sends a message to the player that the server is not connected to the network yet. + * Instead of the server name the server IP will be displayed so the player can join the other server ip manually. + * + * @param player The player to send the message to + * @param serverIP The IP of the server the player should join + */ + public static void sendNotConnectedMessage(@NotNull Player player, String serverIP, String teamName) { + TextComponent comp = new TextComponent("§e" + serverIP + " §7(Click to copy)"); + comp.setClickEvent(new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, serverIP)); + comp.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new ComponentBuilder("§eClick to copy").create())); + + String notConnected = "§cThe team " + teamName + " does not run on Minecraft Version 1.20.5+ yet & is not connected to the network. Meanwhile please use this Server IP to connect to their server:"; + + player.closeInventory(); + player.sendMessage(notConnected); + player.sendMessage(""); + player.spigot().sendMessage(comp); + player.sendMessage(""); + player.sendMessage("§cPress Ctrl + A and Ctrl + C to copy the ip → Press ESC → Back to Menu → Multiplayer → Add Server → Ctrl+V → Done → Join."); + } + + /** + * Sends a message to the player that the build team have no ip specified. + * + * @param player The player to send the message to + * @param buildteam The name of the buildteam the player should join + */ + public static void sendNoIpMessage(@NotNull Player player, String buildteam) { + player.sendMessage("IP of " + buildteam + " is missing in Network API."); + } + + public static @Nullable NavSwitchType determineSwitchPossibilityOrMsgPlayerIfNone(@NotNull Player player, @NotNull BuildTeam targetBuildTeam) { + if (targetBuildTeam.isConnected() && targetBuildTeam.getServerName() != null) { + return NavSwitchType.NETWORK; + } else if (targetBuildTeam.getIP() != null) { + if (isTransferCapable(player, targetBuildTeam)) { + return NavSwitchType.TRANSFER; + } else { + sendNotConnectedMessage(player, targetBuildTeam.getIP(), targetBuildTeam.getName()); + return null; + } + } else { + sendNoIpMessage(player, targetBuildTeam.getName()); + return null; + } + } + + public enum NavSwitchType { + TRANSFER, NETWORK + } + + public static void switchToTeam(BuildTeam team, Player clickPlayer) { + var type = NavUtils.determineSwitchPossibilityOrMsgPlayerIfNone(clickPlayer, team); + + if (type != null) { + if (type == NavUtils.NavSwitchType.NETWORK) { + NavUtils.sendPlayerToConnectedServer(clickPlayer, team.getServerName()); + } else if (type == NavUtils.NavSwitchType.TRANSFER) { + NavUtils.transferPlayer(clickPlayer, team.getIP()); + } + } + } + + public static @NotNull WarpGroup createOtherWarpGroup() { + // Create an "other" Warp Group for warps that don't belong to a warp group + return new WarpGroup(NetworkModule.getInstance().getBuildTeam(), "Other", "Other warps", -1, null); + } +} diff --git a/src/main/java/net/buildtheearth/modules/navigation/NavigationModule.java b/src/main/java/net/buildtheearth/modules/navigation/NavigationModule.java index 508acff6..e903e0da 100644 --- a/src/main/java/net/buildtheearth/modules/navigation/NavigationModule.java +++ b/src/main/java/net/buildtheearth/modules/navigation/NavigationModule.java @@ -3,6 +3,7 @@ import lombok.Getter; import net.buildtheearth.modules.Module; import net.buildtheearth.modules.navigation.components.navigator.NavigatorComponent; +import net.buildtheearth.modules.navigation.components.navigator.commands.BuildteamCommand; import net.buildtheearth.modules.navigation.components.navigator.commands.NavigatorCommand; import net.buildtheearth.modules.navigation.components.navigator.listeners.NavigatorJoinListener; import net.buildtheearth.modules.navigation.components.navigator.listeners.NavigatorOpenListener; @@ -11,6 +12,7 @@ import net.buildtheearth.modules.navigation.components.tpll.listeners.TpllListener; import net.buildtheearth.modules.navigation.components.warps.WarpsComponent; import net.buildtheearth.modules.navigation.components.warps.commands.WarpCommand; +import net.buildtheearth.modules.navigation.components.warps.commands.WarpsBtCommand; import net.buildtheearth.modules.navigation.components.warps.listeners.WarpJoinListener; import net.buildtheearth.modules.network.NetworkModule; @@ -57,6 +59,8 @@ public void enable() { public void registerCommands() { registerCommand("warp", new WarpCommand()); registerCommand("navigator", new NavigatorCommand()); + registerCommand("buildteam", new BuildteamCommand()); + registerCommand("warpsbt", new WarpsBtCommand()); } @Override diff --git a/src/main/java/net/buildtheearth/modules/navigation/components/navigator/NavigatorComponent.java b/src/main/java/net/buildtheearth/modules/navigation/components/navigator/NavigatorComponent.java index 636894c5..bc21d043 100644 --- a/src/main/java/net/buildtheearth/modules/navigation/components/navigator/NavigatorComponent.java +++ b/src/main/java/net/buildtheearth/modules/navigation/components/navigator/NavigatorComponent.java @@ -5,15 +5,16 @@ import net.buildtheearth.BuildTeamTools; import net.buildtheearth.modules.ModuleComponent; import net.buildtheearth.modules.common.CommonModule; -import net.buildtheearth.modules.common.components.dependency.DependencyComponent; import net.buildtheearth.utils.ChatHelper; import net.buildtheearth.utils.Item; import net.buildtheearth.utils.io.ConfigPaths; +import net.buildtheearth.utils.io.ConfigUtil; import org.bukkit.entity.Player; import org.bukkit.inventory.Inventory; import org.bukkit.inventory.ItemStack; import java.lang.reflect.Field; +import java.util.Objects; public class NavigatorComponent extends ModuleComponent { @@ -74,11 +75,11 @@ public void toggle(Player player) { } public ItemStack getItem() { - return Item.create(XMaterial.COMPASS.parseMaterial(), "§aNavigator"); + return Item.edit(Objects.requireNonNull(XMaterial.COMPASS.parseItem()), "§aNavigator"); } public short getSlot() { - return (short) BuildTeamTools.getInstance().getConfig().getInt(ConfigPaths.Navigation.NAVIGATOR_ITEM_SLOT); + return (short) BuildTeamTools.getInstance().getConfig(ConfigUtil.NAVIGATION).getInt(ConfigPaths.Navigation.NAVIGATOR_ITEM_SLOT); } diff --git a/src/main/java/net/buildtheearth/modules/navigation/components/navigator/commands/BuildteamCommand.java b/src/main/java/net/buildtheearth/modules/navigation/components/navigator/commands/BuildteamCommand.java new file mode 100644 index 00000000..3a4c1c46 --- /dev/null +++ b/src/main/java/net/buildtheearth/modules/navigation/components/navigator/commands/BuildteamCommand.java @@ -0,0 +1,69 @@ +package net.buildtheearth.modules.navigation.components.navigator.commands; + +import net.buildtheearth.modules.navigation.NavUtils; +import net.buildtheearth.modules.network.NetworkModule; +import net.buildtheearth.modules.network.model.BuildTeam; +import net.buildtheearth.modules.network.model.Permissions; +import net.buildtheearth.utils.ChatHelper; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.command.TabCompleter; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; + +import java.util.Collections; +import java.util.List; +import java.util.stream.Stream; + +public class BuildteamCommand implements CommandExecutor, TabCompleter { + + @Override + public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) { + return btCommand(sender, label, args, Permissions.NAVIGATOR_USE); + } + + protected boolean btCommand(@NotNull CommandSender sender, @NotNull String label, String[] args, String permission) { + if (!(sender instanceof Player player)) { + sender.sendMessage(ChatHelper.getErrorString("You must be a %s to %s this command!", "player", "execute")); + return true; + } + + if (!player.hasPermission(permission)) { + player.sendMessage(ChatHelper.getErrorString("You don't have permission to use this command!")); + return true; + } + + if (args.length < 1) { + player.sendMessage(ChatHelper.getErrorString("Usage: /%s ", label)); + return true; + } else { + String buildTeamName = args[0]; + var teams = NetworkModule.getInstance().getBuildTeams().stream() + .filter(buildTeam -> buildTeam.getTag().equalsIgnoreCase(buildTeamName) || buildTeam.getBlankName().equalsIgnoreCase(buildTeamName)).toArray(); + if (teams.length == 0 || !(teams[0] instanceof BuildTeam bt)) { + player.sendMessage(ChatHelper.getErrorString("Build team '%s' does not exist!", buildTeamName)); + return true; + } + execute(player, bt); + } + return true; + } + + public void execute(Player player, BuildTeam team) { + NavUtils.switchToTeam(team, player); + } + + @Override + public List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String @NotNull [] args) { + if (args.length >= 1) { + String partial = args[0].toLowerCase(); + return NetworkModule.getInstance().getBuildTeams().stream() + .flatMap(bt -> Stream.of(bt.getTag(), bt.getBlankName())) + .filter(s -> s.toLowerCase().startsWith(partial)) + .toList(); + } + return Collections.emptyList(); + } +} + diff --git a/src/main/java/net/buildtheearth/modules/navigation/components/navigator/listeners/NavigatorJoinListener.java b/src/main/java/net/buildtheearth/modules/navigation/components/navigator/listeners/NavigatorJoinListener.java index 149dfa74..6638b371 100644 --- a/src/main/java/net/buildtheearth/modules/navigation/components/navigator/listeners/NavigatorJoinListener.java +++ b/src/main/java/net/buildtheearth/modules/navigation/components/navigator/listeners/NavigatorJoinListener.java @@ -3,6 +3,7 @@ import net.buildtheearth.BuildTeamTools; import net.buildtheearth.modules.navigation.NavigationModule; import net.buildtheearth.utils.io.ConfigPaths; +import net.buildtheearth.utils.io.ConfigUtil; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; @@ -12,7 +13,7 @@ public class NavigatorJoinListener implements Listener { @EventHandler public void onJoin(PlayerJoinEvent event) { - boolean isEnabled = BuildTeamTools.getInstance().getConfig().getBoolean(ConfigPaths.Navigation.NAVIGATOR_ITEM_ENABLED, false); + boolean isEnabled = BuildTeamTools.getInstance().getConfig(ConfigUtil.NAVIGATION).getBoolean(ConfigPaths.Navigation.NAVIGATOR_ITEM_ENABLED, false); if(!isEnabled) return; diff --git a/src/main/java/net/buildtheearth/modules/navigation/components/tpll/TpllComponent.java b/src/main/java/net/buildtheearth/modules/navigation/components/tpll/TpllComponent.java index 36bba41c..c49435c5 100644 --- a/src/main/java/net/buildtheearth/modules/navigation/components/tpll/TpllComponent.java +++ b/src/main/java/net/buildtheearth/modules/navigation/components/tpll/TpllComponent.java @@ -5,11 +5,15 @@ import com.google.common.io.ByteStreams; import net.buildtheearth.BuildTeamTools; import net.buildtheearth.modules.ModuleComponent; +import net.buildtheearth.modules.navigation.NavUtils; import net.buildtheearth.modules.network.NetworkModule; import net.buildtheearth.utils.ChatHelper; import net.buildtheearth.utils.GeometricUtils; +import org.bukkit.Bukkit; import org.bukkit.Location; +import org.bukkit.NamespacedKey; import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; import java.util.HashMap; import java.util.UUID; @@ -20,6 +24,8 @@ public TpllComponent() { super("Tpll"); } + public static final NamespacedKey TPLL_COOKIE_KEY = NamespacedKey.minecraft("btt_buildteam_tpll"); + /** * Stores a List of the tpll operations that need to happen on join */ @@ -60,9 +66,10 @@ public void processQueueForPlayer(Player player) { ChatHelper.logDebug("Trying to process tpll queue for player: %s", player.getDisplayName()); if(!tpllQueue.containsKey(player.getUniqueId())) return; Location tpllTarget = tpllQueue.get(player.getUniqueId()); - ChatHelper.logDebug("The tpll target is: %s", tpllTarget.toString()); if (tpllTarget == null) return; + ChatHelper.logDebug("The tpll target is: %s", tpllTarget.toString()); + if (tpllTarget.getWorld() == null) { player.sendMessage(ChatHelper.getErrorString("The %s world of this server is %s.", "earth", "unknown")); tpllQueue.remove(player.getUniqueId()); @@ -81,7 +88,7 @@ public void processQueueForPlayer(Player player) { * @param coordinates The coordinates to send the player to on join. * @param targetServerName The server to send the player to. */ - public void tpllPlayer(Player player, double[] coordinates, String targetServerName) { + public void tpllPlayer(@NotNull Player player, double @NotNull [] coordinates, String targetServerName) { ChatHelper.logDebug("Starting universal tpll teleportation for %s to %s.", player.getDisplayName(), targetServerName); // Send a plugin message to the target server which adds the tpll to the queue ByteArrayDataOutput out = ByteStreams.newDataOutput(); @@ -95,4 +102,20 @@ public void tpllPlayer(Player player, double[] coordinates, String targetServerN ChatHelper.logDebug("Teleported player to the target server."); } + public void tpllPlayerTransfer(@NotNull Player player, double @NotNull [] coordinates, String ip) { + ByteArrayDataOutput out = ByteStreams.newDataOutput(); + out.writeDouble(coordinates[0]); + out.writeDouble(coordinates[1]); + player.storeCookie(TPLL_COOKIE_KEY, out.toByteArray()); + NavUtils.transferPlayer(player, ip); + } + + public void processCookie(@NotNull Player player, byte[] cookie) { + ByteArrayDataInput in = ByteStreams.newDataInput(cookie); + double targetLatitude = in.readDouble(); + double targetLongitude = in.readDouble(); + ChatHelper.logDebug("Processing cookie for tpll event: lat: %s lon: %s", targetLatitude, targetLongitude); + Bukkit.getScheduler().runTask(BuildTeamTools.getInstance(), // Needs to be delayed if not exception will be trown and nothing happens + () -> player.performCommand("tpll " + targetLatitude + " " + targetLongitude)); + } } diff --git a/src/main/java/net/buildtheearth/modules/navigation/components/tpll/listeners/TpllJoinListener.java b/src/main/java/net/buildtheearth/modules/navigation/components/tpll/listeners/TpllJoinListener.java index 00efd611..af8b28d5 100644 --- a/src/main/java/net/buildtheearth/modules/navigation/components/tpll/listeners/TpllJoinListener.java +++ b/src/main/java/net/buildtheearth/modules/navigation/components/tpll/listeners/TpllJoinListener.java @@ -1,6 +1,7 @@ package net.buildtheearth.modules.navigation.components.tpll.listeners; import net.buildtheearth.modules.navigation.NavigationModule; +import net.buildtheearth.modules.navigation.components.tpll.TpllComponent; import net.buildtheearth.utils.ChatHelper; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; @@ -12,5 +13,14 @@ public class TpllJoinListener implements Listener { public void onJoin(PlayerJoinEvent event) { ChatHelper.logDebug("Player joined. Processing tpll queue for them. Event fired."); NavigationModule.getInstance().getTpllComponent().processQueueForPlayer(event.getPlayer()); + event.getPlayer().retrieveCookie(TpllComponent.TPLL_COOKIE_KEY).thenAcceptAsync(cookie -> { + if (cookie != null) { + ChatHelper.logDebug("Player has a TPLL cookie, processing it."); + NavigationModule.getInstance().getTpllComponent().processCookie(event.getPlayer(), cookie); + event.getPlayer().storeCookie(TpllComponent.TPLL_COOKIE_KEY, new byte[0]); // Reset the cookie + } else { + ChatHelper.logDebug("Player does not have a TPLL cookie."); + } + }); } } diff --git a/src/main/java/net/buildtheearth/modules/navigation/components/tpll/listeners/TpllListener.java b/src/main/java/net/buildtheearth/modules/navigation/components/tpll/listeners/TpllListener.java index c937526c..0f4d0934 100644 --- a/src/main/java/net/buildtheearth/modules/navigation/components/tpll/listeners/TpllListener.java +++ b/src/main/java/net/buildtheearth/modules/navigation/components/tpll/listeners/TpllListener.java @@ -1,14 +1,19 @@ package net.buildtheearth.modules.navigation.components.tpll.listeners; +import com.alpsbte.alpslib.utils.AlpsUtils; +import net.buildtheearth.modules.navigation.NavUtils; +import net.buildtheearth.modules.navigation.NavigationModule; import net.buildtheearth.modules.network.NetworkModule; import net.buildtheearth.modules.network.api.OpenStreetMapAPI; +import net.buildtheearth.modules.network.model.BuildTeam; import net.buildtheearth.modules.network.model.Region; -import net.buildtheearth.modules.navigation.NavigationModule; import net.buildtheearth.utils.ChatHelper; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerCommandPreprocessEvent; +import org.jetbrains.annotations.NotNull; +import java.util.Objects; import java.util.concurrent.CompletableFuture; @@ -27,7 +32,7 @@ public class TpllListener implements Listener { private double lat; // Target server name for teleportation - private String targetServerName; + private BuildTeam targetBuildTeam; @EventHandler public void onTpll(PlayerCommandPreprocessEvent event) { @@ -37,12 +42,23 @@ public void onTpll(PlayerCommandPreprocessEvent event) { // Check if the command is a TPLL command if (!isTpllCommand(event)) return; + ChatHelper.logDebug("Intercepted TPLL command wit lat and lon: %s %s", lat, lon); + // Check if teleportation interception is required - shouldIntercept(event).thenAcceptAsync(shouldIntercept -> { + shouldIntercept().thenAcceptAsync(shouldIntercept -> { // If interception is required, cancel the event and perform cross teleportation - if (shouldIntercept) { - event.setCancelled(true); - NavigationModule.getInstance().getTpllComponent().tpllPlayer(event.getPlayer(), new double[]{lat, lon}, targetServerName); + if (Boolean.TRUE.equals(shouldIntercept)) { + var type = NavUtils.determineSwitchPossibilityOrMsgPlayerIfNone(event.getPlayer(), targetBuildTeam); + if (type != null) { + if (type == NavUtils.NavSwitchType.NETWORK) { + NavigationModule.getInstance().getTpllComponent().tpllPlayer(event.getPlayer(), + new double[]{lat, lon}, targetBuildTeam.getServerName()); + } else if (type == NavUtils.NavSwitchType.TRANSFER) { + NavigationModule.getInstance().getTpllComponent().tpllPlayerTransfer(event.getPlayer(), + new double[]{lat, lon}, targetBuildTeam.getIP()); + } + event.setCancelled(true); + } } }); } @@ -53,7 +69,7 @@ public void onTpll(PlayerCommandPreprocessEvent event) { * @param event The event triggered when a player processes a command. * @return True if the command is a TPLL command and coordinates are extracted sucessfully, false otherwise. */ - private boolean isTpllCommand(PlayerCommandPreprocessEvent event) { + private boolean isTpllCommand(@NotNull PlayerCommandPreprocessEvent event) { // Check if the command starts with "tpll" ChatHelper.logDebug(event.getMessage()); if (!event.getMessage().startsWith("/tpll")) return false; @@ -61,23 +77,28 @@ private boolean isTpllCommand(PlayerCommandPreprocessEvent event) { // Split the command to extract coordinates String[] splitMessage = event.getMessage().split(" "); - splitMessage[1] = splitMessage[1].replaceAll(",", " ").trim(); if (splitMessage.length < 3) return false; + splitMessage[1] = splitMessage[1].replace(",", " ").trim(); ChatHelper.logDebug("Command had the correct length (%s).", splitMessage.length); - // Extract and set latitude and longitude coordinates - this.lat = Double.parseDouble(splitMessage[1]); - this.lon = Double.parseDouble(splitMessage[2]); + Double oLat = AlpsUtils.tryParseDouble(splitMessage[1]); + Double oLon = AlpsUtils.tryParseDouble(splitMessage[2]); + if (oLat == null || oLon == null) { + ChatHelper.logDebug("Command did not contain valid coordinates."); + return false; + } + + this.lat = oLat; + this.lon = oLon; return true; } /** * Determines if teleportation interception is required based on the player's location and network status. * - * @param event The event triggered when a player processes a command. * @return A CompletableFuture representing whether teleportation interception is required. */ - private CompletableFuture shouldIntercept(PlayerCommandPreprocessEvent event) { + private @NotNull CompletableFuture shouldIntercept() { return OpenStreetMapAPI.getCountryFromLocationAsync(new double[]{lat, lon}) .thenComposeAsync(address -> { if (address == null) return CompletableFuture.completedFuture(false); @@ -85,14 +106,8 @@ private CompletableFuture shouldIntercept(PlayerCommandPreprocessEvent String countryName = address[0]; Region region = Region.getByName(countryName); - if (!networkModule.getBuildTeam().isConnected() || !region.isConnected()) { - event.getPlayer().sendMessage(ChatHelper.getErrorString("Either this server or the receiving server isn't connected to the network.")); - event.setCancelled(true); - return CompletableFuture.completedFuture(false); - } - - if (region.getBuildTeam().getID() != networkModule.getBuildTeam().getID()) { - targetServerName = region.getBuildTeam().getServerName(); + if (!Objects.equals(region.getBuildTeam().getID(), networkModule.getBuildTeam().getID())) { + targetBuildTeam = region.getBuildTeam(); return CompletableFuture.completedFuture(true); } diff --git a/src/main/java/net/buildtheearth/modules/navigation/components/warps/WarpsComponent.java b/src/main/java/net/buildtheearth/modules/navigation/components/warps/WarpsComponent.java index 27a2e773..0064ace7 100644 --- a/src/main/java/net/buildtheearth/modules/navigation/components/warps/WarpsComponent.java +++ b/src/main/java/net/buildtheearth/modules/navigation/components/warps/WarpsComponent.java @@ -5,21 +5,27 @@ import com.google.common.io.ByteStreams; import net.buildtheearth.BuildTeamTools; import net.buildtheearth.modules.ModuleComponent; +import net.buildtheearth.modules.navigation.NavUtils; +import net.buildtheearth.modules.navigation.components.warps.menu.WarpEditMenu; +import net.buildtheearth.modules.navigation.components.warps.menu.WarpGroupEditMenu; +import net.buildtheearth.modules.navigation.components.warps.menu.WarpGroupMenu; +import net.buildtheearth.modules.navigation.components.warps.menu.WarpMenu; +import net.buildtheearth.modules.navigation.components.warps.model.Warp; +import net.buildtheearth.modules.navigation.components.warps.model.WarpGroup; import net.buildtheearth.modules.network.NetworkModule; -import net.buildtheearth.modules.network.api.NetworkAPI; import net.buildtheearth.modules.network.api.OpenStreetMapAPI; import net.buildtheearth.modules.network.model.BuildTeam; import net.buildtheearth.utils.ChatHelper; import net.buildtheearth.utils.GeometricUtils; import net.buildtheearth.utils.geo.CoordinateConversion; -import net.buildtheearth.modules.navigation.components.warps.menu.WarpEditMenu; -import net.buildtheearth.modules.navigation.components.warps.menu.WarpGroupEditMenu; -import net.buildtheearth.modules.navigation.components.warps.model.Warp; -import net.buildtheearth.modules.navigation.components.warps.model.WarpGroup; +import net.buildtheearth.utils.menus.AbstractMenu; import org.bukkit.Bukkit; import org.bukkit.Location; +import org.bukkit.NamespacedKey; import org.bukkit.World; import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.HashMap; import java.util.UUID; @@ -27,6 +33,8 @@ public class WarpsComponent extends ModuleComponent { + public static final NamespacedKey WARP_COOKIE_KEY = NamespacedKey.minecraft("btt_warp"); + public WarpsComponent() { super("Warps"); } @@ -42,14 +50,10 @@ public WarpsComponent() { * @param in The ByteArray received through the PluginMessageChannel * @param player The player to whom the warp operation belongs */ - public void addWarpToQueue(ByteArrayDataInput in, Player player) { - //Check the target server - String targetServer = in.readUTF(); - if (targetServer.equals(NetworkModule.getInstance().getBuildTeam().getServerName())) { - //Extracts the warp key from the plugin message + public void addWarpToQueue(@NotNull ByteArrayDataInput in, Player player) { + // Extracts the warp key from the plugin message String warpKey = in.readUTF(); - - Warp warp = NetworkAPI.getWarpByKey(warpKey); + Warp warp = getWarpByKey(warpKey); if (warp == null) { player.sendMessage(ChatHelper.getErrorString("The warp you tried to warp to does not exist anymore.")); @@ -62,7 +66,6 @@ public void addWarpToQueue(ByteArrayDataInput in, Player player) { // Adds the event to the list, to be dealt with by the join listener warpQueue.put(player.getUniqueId(), targetWarpLocation); - } } /** @@ -92,9 +95,10 @@ public void processQueueForPlayer(Player player) { * @param player The player to warp * @param warp The warp to teleport the player to */ - public void warpPlayer(Player player, Warp warp) { + public void warpPlayer(Player player, @NotNull Warp warp) { // If the warp is in the same team, just teleport the player if(warp.getWarpGroup().getBuildTeam().getID().equals(NetworkModule.getInstance().getBuildTeam().getID())) { + ChatHelper.logDebug("Warping player %s to warp %s", player.getName(), warp.getName()); Location loc = GeometricUtils.getLocationFromCoordinatesYawPitch(new double[]{warp.getLat(), warp.getLon()}, warp.getYaw(), warp.getPitch()); if(loc.getWorld() == null) { @@ -104,34 +108,50 @@ public void warpPlayer(Player player, Warp warp) { loc.setY(warp.getY()); - player.teleport(loc); + player.teleportAsync(loc); ChatHelper.sendSuccessfulMessage(player, "Successfully warped you to %s.", warp.getName()); - return; } - // Get the server the warp is on - String targetServer = "Empty"; // NetworkAPI.getServerNameByCountryCode(warp.getCountryCode()); + ChatHelper.logDebug("Determining switch possibility for warp %s, because it's the wrong server...", warp.getName()); + + var type = NavUtils.determineSwitchPossibilityOrMsgPlayerIfNone(player, warp.getWarpGroup().getBuildTeam()); - // Send a plugin message to that server which adds the warp to the queue - ByteArrayDataOutput out = ByteStreams.newDataOutput(); - out.writeUTF("UniversalWarps"); - out.writeUTF(targetServer); - out.writeUTF(warp.getId().toString()); + if (type == NavUtils.NavSwitchType.NETWORK) { + // Send a plugin message to that server which adds the warp to the queue + ByteArrayDataOutput out = ByteStreams.newDataOutput(); + out.writeUTF("UniversalWarps"); + out.writeUTF(warp.getId().toString()); - player.sendPluginMessage(BuildTeamTools.getInstance(), "btt:buildteam", out.toByteArray()); + player.sendPluginMessage(BuildTeamTools.getInstance(), "btt:buildteam", out.toByteArray()); - // Switch the player to the target server - NetworkModule.getInstance().switchServer(player, targetServer); + // Switch the player to the target server + NetworkModule.getInstance().switchServer(player, warp.getWarpGroup().getBuildTeam().getServerName()); + } else if (type == NavUtils.NavSwitchType.TRANSFER) { + ByteArrayDataOutput out = ByteStreams.newDataOutput(); + out.writeUTF(String.valueOf(warp.getId())); + player.storeCookie(WARP_COOKIE_KEY, out.toByteArray()); + NavUtils.transferPlayer(player, warp.getWarpGroup().getBuildTeam().getIP()); + } } + public WarpGroup getOtherWarpGroup() { + return NetworkModule.getInstance().getBuildTeam().getWarpGroups().stream().filter(warpGroup -> warpGroup.getName().equalsIgnoreCase("Other")).findFirst().orElse(null); + } + public void createWarp(Player creator) { + WarpGroup group = getOtherWarpGroup(); + if (group == null) { + group = NavUtils.createOtherWarpGroup(); + } + createWarp(creator, group); + } /** Creates a warp at the player's location and opens the warp edit menu. * * @param creator The player that is creating the warp */ - public void createWarp(Player creator){ + public void createWarp(Player creator, WarpGroup group) { // Get the geographic coordinates of the player's location. Location location = creator.getLocation(); double[] coordinates = CoordinateConversion.convertToGeo(location.getX(), location.getZ()); @@ -151,27 +171,24 @@ public void createWarp(Player creator){ return; } - // Get the Other Group for default warp group - WarpGroup group = NetworkModule.getInstance().getBuildTeam().getWarpGroups().stream().filter(warpGroup -> warpGroup.getName().equalsIgnoreCase("Other")).findFirst().orElse(null); - // Create a default name for the warp String name = creator.getName() + "'s Warp"; // Create an instance of the warp POJO Warp warp = new Warp(group, name, countryCodeCCA2, "cca2", null, null, null, location.getWorld().getName(), coordinates[0], coordinates[1], location.getY(), location.getYaw(), location.getPitch(), false); - // Create the actual warp - new WarpEditMenu(creator, warp, false, true); + Bukkit.getScheduler().runTask(BuildTeamTools.getInstance(), () -> + new WarpEditMenu(creator, warp, false, true)); }).exceptionally(e -> { - creator.sendMessage(ChatHelper.getErrorString("An error occurred while creating the warp!")); - e.printStackTrace(); + creator.sendMessage(ChatHelper.getErrorString("An error occurred while creating the warp! %s", e.getMessage())); + BuildTeamTools.getInstance().getComponentLogger().error("An error occurred while creating the warp!", e); return null; }); } - public void createWarpGroup(Player creator){ + public void createWarpGroup(@NotNull Player creator) { // Create a default name for the warp String name = creator.getName() + "'s Warp Group"; String description = "This is a warp group."; @@ -191,8 +208,35 @@ public Warp getWarpByName(String name){ return getWarpByName(NetworkModule.getInstance().getBuildTeam(), name); } - public Warp getWarpByName(BuildTeam buildTeam, String name) { + public Warp getWarpByName(@NotNull BuildTeam buildTeam, String name) { return buildTeam.getWarpGroups().stream().flatMap(warpGroup -> warpGroup.getWarps().stream()) .filter(warp1 -> warp1.getName() != null && warp1.getName().equalsIgnoreCase(name)).findFirst().orElse(null); } + + public void processCookie(@NotNull Player player, byte[] cookie) { + ByteArrayDataInput in = ByteStreams.newDataInput(cookie); + Warp warp = getWarpByKey(in.readUTF()); + ChatHelper.logDebug("Processing cookie for warp %s", warp.getName()); + warpPlayer(player, warp); + } + + public Warp getWarpByKey(String key) { + ChatHelper.logDebug("Retrieving warp with key %s", key); + return NetworkModule.getInstance().getBuildTeam().getWarpGroups().stream().flatMap(warpGroup -> warpGroup.getWarps().stream()) + .filter(warp1 -> warp1.getId().toString().equals(key)).findFirst().orElse(null); + } + + public static void openWarpMenu(@NotNull Player player) { + openWarpMenu(player, NetworkModule.getInstance().getBuildTeam(), null); + } + + public static void openWarpMenu(@NotNull Player player, @NotNull BuildTeam buildTeam, @Nullable AbstractMenu menu) { + int warpGroupCount = buildTeam.getWarpGroups().size(); + + switch (warpGroupCount) { + case 0 -> player.sendMessage(ChatHelper.getErrorString("This server does not have any warps yet!")); + case 1 -> new WarpMenu(player, buildTeam.getWarpGroups().getFirst(), false, true); + default -> new WarpGroupMenu(player, buildTeam, menu != null, true, menu); + } + } } diff --git a/src/main/java/net/buildtheearth/modules/navigation/components/warps/commands/WarpCommand.java b/src/main/java/net/buildtheearth/modules/navigation/components/warps/commands/WarpCommand.java index de874415..c7363d17 100644 --- a/src/main/java/net/buildtheearth/modules/navigation/components/warps/commands/WarpCommand.java +++ b/src/main/java/net/buildtheearth/modules/navigation/components/warps/commands/WarpCommand.java @@ -1,8 +1,7 @@ package net.buildtheearth.modules.navigation.components.warps.commands; import net.buildtheearth.modules.navigation.NavigationModule; -import net.buildtheearth.modules.navigation.components.warps.menu.WarpGroupMenu; -import net.buildtheearth.modules.navigation.components.warps.menu.WarpMenu; +import net.buildtheearth.modules.navigation.components.warps.WarpsComponent; import net.buildtheearth.modules.navigation.components.warps.model.Warp; import net.buildtheearth.modules.network.NetworkModule; import net.buildtheearth.modules.network.model.Permissions; @@ -10,13 +9,19 @@ import org.bukkit.command.Command; import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandSender; +import org.bukkit.command.TabCompleter; import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; -public class WarpCommand implements CommandExecutor { +import java.util.Collections; +import java.util.List; + +public class WarpCommand implements CommandExecutor, TabCompleter { @Override - public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { - if (!(sender instanceof Player)) { + public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) { + if (!(sender instanceof Player player)) { sender.sendMessage(ChatHelper.getErrorString("This command can only be used by a player!")); return true; } @@ -27,37 +32,26 @@ public boolean onCommand(CommandSender sender, Command command, String label, St return true; } - Player player = (Player) sender; - // If no arguments were supplied assume the player wants to open the warp menu if (args.length == 0) { - int warpGroupCount = NetworkModule.getInstance().getBuildTeam().getWarpGroups().size(); - - if(warpGroupCount == 0){ - player.sendMessage(ChatHelper.getErrorString("This server does not have any warps yet!")); - return true; - }else if(warpGroupCount == 1) - new WarpMenu(player, NetworkModule.getInstance().getBuildTeam().getWarpGroups().get(0), false, true); - else - new WarpGroupMenu(player, NetworkModule.getInstance().getBuildTeam(), false, true); - + if (checkForWarpUsePermissionAndMessage(player)) WarpsComponent.openWarpMenu(player); return true; } // WARP CREATE if (args[0].equalsIgnoreCase("create")) { - // Check if the command has only one argument - if (args.length > 1){ - player.sendMessage(ChatHelper.getErrorString("Usage: /warp create")); - return true; - } - // Check if the player has the required permissions if (!player.hasPermission(Permissions.WARP_CREATE)) { player.sendMessage(ChatHelper.getErrorString("You don't have the required %s to %s warps.", "permission", "create")); return true; } + // Check if the command has only one argument + if (args.length > 1) { + player.sendMessage(ChatHelper.getErrorString("Usage: /warp create")); + return true; + } + player.sendActionBar(ChatHelper.getStandardString(false, "Creating the warp...")); NavigationModule.getInstance().getWarpsComponent().createWarp(player); @@ -68,11 +62,7 @@ public boolean onCommand(CommandSender sender, Command command, String label, St // Combine the args to one warp name String key = String.join(" ", args); - // Check if the player has the required permission - if (!player.hasPermission(Permissions.WARP_USE)) { - player.sendMessage(ChatHelper.getErrorString("You don't have the required %s to %s warps.", "permission", "use")); - return true; - } + if (!checkForWarpUsePermissionAndMessage(player)) return true; // Find the warp with the given key Warp warp = NavigationModule.getInstance().getWarpsComponent().getWarpByName(key); @@ -86,4 +76,26 @@ public boolean onCommand(CommandSender sender, Command command, String label, St return true; } + + private static boolean checkForWarpUsePermissionAndMessage(@NotNull Player player) { + // Check if the player has the required permission + if (!player.hasPermission(Permissions.WARP_USE)) { + player.sendMessage(ChatHelper.getErrorString("You don't have the required %s to %s warps.", "permission", "use")); + return false; + } + return true; + } + + @Override + public @Nullable List onTabComplete(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, @NotNull String @NotNull [] args) { + if (args.length == 1) { + String partial = args[0].toLowerCase(); + return NetworkModule.getInstance().getBuildTeam().getWarpGroups().stream() + .flatMap(gr -> gr.getWarps().stream().map(Warp::getName)) + .filter(s -> s.toLowerCase().startsWith(partial)) + .toList(); + } + return Collections.emptyList(); + + } } diff --git a/src/main/java/net/buildtheearth/modules/navigation/components/warps/commands/WarpsBtCommand.java b/src/main/java/net/buildtheearth/modules/navigation/components/warps/commands/WarpsBtCommand.java new file mode 100644 index 00000000..e7336706 --- /dev/null +++ b/src/main/java/net/buildtheearth/modules/navigation/components/warps/commands/WarpsBtCommand.java @@ -0,0 +1,25 @@ +package net.buildtheearth.modules.navigation.components.warps.commands; + +import net.buildtheearth.modules.navigation.components.navigator.commands.BuildteamCommand; +import net.buildtheearth.modules.navigation.components.warps.WarpsComponent; +import net.buildtheearth.modules.network.model.BuildTeam; +import net.buildtheearth.modules.network.model.Permissions; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.command.TabCompleter; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; + +public class WarpsBtCommand extends BuildteamCommand implements CommandExecutor, TabCompleter { + @Override + public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label, String[] args) { + return btCommand(sender, label, args, Permissions.WARP_USE); + } + + @Override + public void execute(Player player, BuildTeam team) { + WarpsComponent.openWarpMenu(player, team, null); + } +} + diff --git a/src/main/java/net/buildtheearth/modules/navigation/components/warps/listeners/WarpJoinListener.java b/src/main/java/net/buildtheearth/modules/navigation/components/warps/listeners/WarpJoinListener.java index 44b1ede7..6771a91c 100644 --- a/src/main/java/net/buildtheearth/modules/navigation/components/warps/listeners/WarpJoinListener.java +++ b/src/main/java/net/buildtheearth/modules/navigation/components/warps/listeners/WarpJoinListener.java @@ -1,14 +1,27 @@ package net.buildtheearth.modules.navigation.components.warps.listeners; import net.buildtheearth.modules.navigation.NavigationModule; +import net.buildtheearth.modules.navigation.components.warps.WarpsComponent; +import net.buildtheearth.utils.ChatHelper; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerJoinEvent; +import org.jetbrains.annotations.NotNull; public class WarpJoinListener implements Listener { @EventHandler - public void onJoin(PlayerJoinEvent event) { + public void onJoin(@NotNull PlayerJoinEvent event) { + ChatHelper.logDebug("Player joined. Processing warp queue for them. Event fired."); NavigationModule.getInstance().getWarpsComponent().processQueueForPlayer(event.getPlayer()); + event.getPlayer().retrieveCookie(WarpsComponent.WARP_COOKIE_KEY).thenAcceptAsync(cookie -> { + if (cookie != null) { + ChatHelper.logDebug("Player has a warp cookie, processing it."); + NavigationModule.getInstance().getWarpsComponent().processCookie(event.getPlayer(), cookie); + event.getPlayer().storeCookie(WarpsComponent.WARP_COOKIE_KEY, new byte[0]); // Reset the cookie + } else { + ChatHelper.logDebug("Player does not have a warp cookie."); + } + }); } } diff --git a/src/main/java/net/buildtheearth/modules/navigation/components/warps/menu/WarpGroupEditMenu.java b/src/main/java/net/buildtheearth/modules/navigation/components/warps/menu/WarpGroupEditMenu.java index 51e0c1fe..2abc56e2 100644 --- a/src/main/java/net/buildtheearth/modules/navigation/components/warps/menu/WarpGroupEditMenu.java +++ b/src/main/java/net/buildtheearth/modules/navigation/components/warps/menu/WarpGroupEditMenu.java @@ -1,5 +1,6 @@ package net.buildtheearth.modules.navigation.components.warps.menu; +import com.alpsbte.alpslib.utils.AlpsUtils; import com.cryptomorin.xseries.XMaterial; import net.buildtheearth.BuildTeamTools; import net.buildtheearth.modules.network.NetworkModule; @@ -23,18 +24,17 @@ public class WarpGroupEditMenu extends AbstractMenu { - public static String WARP_GROUP_UPDATE_INV_NAME = "Configure the Warp Group"; + private static final String WARP_GROUP_UPDATE_INV_NAME = "Configure the Warp Group"; + private static final int WARP_GROUP = 4; + private static final int NAME_SLOT = 18; + private static final int DESCRIPTION_SLOT = 20; - public static int WARP_GROUP = 4; - public static int NAME_SLOT = 18; - public static int DESCRIPTION_SLOT = 20; + private static final int SLOT_SLOT = 22; - public static int SLOT_SLOT = 22; + private static final int MATERIAL_SLOT = 24; - public static int MATERIAL_SLOT = 24; - - public static int DELETE_SLOT = 26; + private static final int DELETE_SLOT = 26; private final WarpGroup warpGroup; @@ -70,22 +70,22 @@ protected void setMenuItemsAsync() { // Set the name item ArrayList nameLore = ListUtil.createList("", "§eCurrent Name: ", warpGroup.getName()); - getMenu().getSlot(NAME_SLOT).setItem(Item.create(XMaterial.NAME_TAG.parseMaterial(), "§6§lChange Name", nameLore)); + getMenu().getSlot(NAME_SLOT).setItem(Item.create(XMaterial.NAME_TAG.get(), "§6§lChange Name", nameLore)); // Set the Description item - getMenu().getSlot(DESCRIPTION_SLOT).setItem(Item.create(XMaterial.BOOK.parseMaterial(), "§6§lChange Description", warpGroup.getDescriptionLore())); + getMenu().getSlot(DESCRIPTION_SLOT).setItem(Item.create(XMaterial.BOOK.get(), "§6§lChange Description", warpGroup.getDescriptionLore())); // Set the Slot item int slot = warpGroup.getSlot(); ArrayList slotLore = ListUtil.createList("", "§eSlot: ", slot < 0 || slot >= 27 ? "§7Auto" : String.valueOf(slot)); - getMenu().getSlot(SLOT_SLOT).setItem(new Item(XMaterial.ITEM_FRAME.parseMaterial()).setDisplayName("§6§lChange Slot").setLore(slotLore).build()); + getMenu().getSlot(SLOT_SLOT).setItem(new Item(XMaterial.ITEM_FRAME.get()).setDisplayName("§6§lChange Slot").setLore(slotLore).build()); // Set the Material item ArrayList materialLore = ListUtil.createList("", "§eMaterial: ", warpGroup.getMaterial() == null ? "§7Default" : warpGroup.getMaterial()); getMenu().getSlot(MATERIAL_SLOT).setItem(new Item(warpGroup.getMaterialItem()).setDisplayName("§6§lChange Material").setLore(materialLore).build()); // Set the delete item - getMenu().getSlot(DELETE_SLOT).setItem(Item.create(XMaterial.BARRIER.parseMaterial(), "§c§lDelete Warp Group", ListUtil.createList("", "§8Click to delete the warp group."))); + getMenu().getSlot(DELETE_SLOT).setItem(Item.create(XMaterial.BARRIER.get(), "§c§lDelete Warp Group", ListUtil.createList("", "§8Click to delete the warp group."))); } @Override @@ -125,7 +125,7 @@ protected void setItemClickEventsAsync() { ); }) .text("Name") - .itemLeft(Item.create(XMaterial.NAME_TAG.parseMaterial(), "§6§lChange Name")) + .itemLeft(Item.create(XMaterial.NAME_TAG.get(), "§6§lChange Name")) .title("§8Change the warp name") .plugin(BuildTeamTools.getInstance()) .open(clickPlayer); @@ -140,7 +140,7 @@ protected void setItemClickEventsAsync() { BookMenu bookMenu = new BookMenu(clickPlayer, "§6§lChange Description", clickPlayer.getName(), description, 240); - bookMenu.onComplete((text) -> { + bookMenu.onComplete(text -> { if(text == null) { clickPlayer.sendMessage("§cA problem occurred while saving the description."); new WarpGroupEditMenu(clickPlayer, warpGroup, alreadyExists, true); @@ -148,9 +148,9 @@ protected void setItemClickEventsAsync() { } // Combine the first page to a single string - StringBuilder finalText = new StringBuilder(text.get(0)[0]); - for(int i = 1; i < text.get(0).length; i++) - finalText.append("
").append(text.get(0)[i]); + StringBuilder finalText = new StringBuilder(text.getFirst()[0]); + for(int i = 1; i < text.getFirst().length; i++) + finalText.append("
").append(text.getFirst()[i]); warpGroup.setDescription(finalText.toString()); new WarpGroupEditMenu(clickPlayer, warpGroup, alreadyExists, true); @@ -170,12 +170,14 @@ protected void setItemClickEventsAsync() { if (slot != AnvilGUI.Slot.OUTPUT) return Collections.emptyList(); + Integer selectedSlot = AlpsUtils.tryParseInt(stateSnapshot.getText()); + // Make sure that the slot is a valid number and between 0 and 26 if(!stateSnapshot.getText().matches("-?\\d+") - || Integer.parseInt(stateSnapshot.getText()) < -1 - || Integer.parseInt(stateSnapshot.getText()) > 26){ - return Arrays.asList( - AnvilGUI.ResponseAction.close(), + || selectedSlot == null + || selectedSlot < -1 + || selectedSlot > 26) { + return Arrays.asList(AnvilGUI.ResponseAction.close(), AnvilGUI.ResponseAction.run(() -> { clickPlayer.closeInventory(); stateSnapshot.getPlayer().playSound(clickPlayer.getLocation(), Sound.ENTITY_ITEM_BREAK, 1.0F, 1.0F); @@ -185,17 +187,34 @@ protected void setItemClickEventsAsync() { ); } + var warpGroups = warpGroup.getBuildTeam().getWarpGroups().stream() + .filter(g -> g.getSlot() == selectedSlot) + .map(WarpGroup::getName) + .findFirst(); + + if(selectedSlot != -1 && warpGroups.isPresent()) { + return Arrays.asList(AnvilGUI.ResponseAction.close(), + AnvilGUI.ResponseAction.run(() -> { + clickPlayer.closeInventory(); + stateSnapshot.getPlayer().playSound(clickPlayer.getLocation(), Sound.ENTITY_ITEM_BREAK, 1.0F, 1.0F); + clickPlayer.sendMessage("§cThe warp group " + warpGroups.get() + + " already have the slot " + selectedSlot + " occupied."); + new WarpGroupEditMenu(clickPlayer, warpGroup, alreadyExists, true); + }) + ); + } + stateSnapshot.getPlayer().playSound(clickPlayer.getLocation(), Sound.ENTITY_PLAYER_LEVELUP, 1.0F, 1.0F); return Arrays.asList( AnvilGUI.ResponseAction.close(), AnvilGUI.ResponseAction.run(() -> { - warpGroup.setSlot(Integer.parseInt(stateSnapshot.getText())); + warpGroup.setSlot(selectedSlot); new WarpGroupEditMenu(clickPlayer, warpGroup, alreadyExists, true); }) ); }) .text("Enter slot 0-26. Set -1 for auto.") - .itemLeft(Item.create(XMaterial.ITEM_FRAME.parseMaterial(), "§6§lChange Slot")) + .itemLeft(Item.create(XMaterial.ITEM_FRAME.get(), "§6§lChange Slot")) .title("§8Change the warp slot") .plugin(BuildTeamTools.getInstance()) .open(clickPlayer); diff --git a/src/main/java/net/buildtheearth/modules/navigation/components/warps/menu/WarpGroupMenu.java b/src/main/java/net/buildtheearth/modules/navigation/components/warps/menu/WarpGroupMenu.java index 2af79a27..235e32c4 100644 --- a/src/main/java/net/buildtheearth/modules/navigation/components/warps/menu/WarpGroupMenu.java +++ b/src/main/java/net/buildtheearth/modules/navigation/components/warps/menu/WarpGroupMenu.java @@ -1,36 +1,47 @@ package net.buildtheearth.modules.navigation.components.warps.menu; +import com.google.gson.Gson; +import net.buildtheearth.BuildTeamTools; import net.buildtheearth.modules.navigation.NavigationModule; -import net.buildtheearth.modules.navigation.menu.CountrySelectorMenu; +import net.buildtheearth.modules.navigation.components.warps.model.WarpGroup; +import net.buildtheearth.modules.network.NetworkModule; import net.buildtheearth.modules.network.model.BuildTeam; import net.buildtheearth.modules.network.model.Permissions; +import net.buildtheearth.utils.ChatHelper; import net.buildtheearth.utils.CustomHeads; import net.buildtheearth.utils.Item; import net.buildtheearth.utils.ListUtil; import net.buildtheearth.utils.MenuItems; +import net.buildtheearth.utils.io.ConfigPaths; +import net.buildtheearth.utils.io.ConfigUtil; +import net.buildtheearth.utils.menus.AbstractMenu; import net.buildtheearth.utils.menus.AbstractPaginatedMenu; -import net.buildtheearth.modules.navigation.components.warps.model.WarpGroup; import org.bukkit.entity.Player; import org.ipvp.canvas.mask.BinaryMask; import org.ipvp.canvas.mask.Mask; +import org.jetbrains.annotations.NotNull; +import java.util.ArrayDeque; +import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; public class WarpGroupMenu extends AbstractPaginatedMenu { - public final int BACK_ITEM_SLOT = 27; + public static final int BACK_ITEM_SLOT = 27; - public final int ALTERNATE_PLUS_SLOT = 35; + public static final int ALTERNATE_PLUS_SLOT = 35; private final boolean hasBackItem; private final BuildTeam buildTeam; + private int plusSlot = ALTERNATE_PLUS_SLOT; + private AbstractMenu backMenue; /** In this menu the player can select a warp group to view the warps in each warp group. * * @param menuPlayer The player that is viewing the menu * @param buildTeam The build team that the menu is for - * @param hasBackItem Whether the menu has a back item + * @param hasBackItem Whether the menu has a back item - only true on overrides or when country selector menu */ public WarpGroupMenu(Player menuPlayer, BuildTeam buildTeam, boolean hasBackItem, boolean autoLoad) { super(4, 3, "Warp Menu", menuPlayer, autoLoad); @@ -38,51 +49,52 @@ public WarpGroupMenu(Player menuPlayer, BuildTeam buildTeam, boolean hasBackItem this.buildTeam = buildTeam; } + public WarpGroupMenu(Player menuPlayer, BuildTeam buildTeam, boolean hasBackItem, boolean autoLoad, AbstractMenu menu) { + this(menuPlayer, buildTeam, hasBackItem, autoLoad); + this.backMenue = menu; + } + @Override protected void setMenuItemsAsync() { - if(hasBackItem) - setBackItem(BACK_ITEM_SLOT, new CountrySelectorMenu(getMenuPlayer(), buildTeam.getContinent(), false)); + if (hasBackItem) { + ChatHelper.logDebug("Setting back item for warp group menu"); + setBackItem(BACK_ITEM_SLOT, backMenue); + } } @Override protected void setItemClickEventsAsync() { - if(hasBackItem) - getMenu().getSlot(BACK_ITEM_SLOT).setClickHandler((clickPlayer, clickInformation) -> { - clickPlayer.closeInventory(); - - }); + // No items need to be set here } @Override - protected void setPaginatedPreviewItems(List source) { - List warpGroups = source.stream().map(l -> (WarpGroup) l).collect(Collectors.toList()); + protected void setPaginatedPreviewItems(@NotNull List source) { + List warpGroups = source.stream().map(l -> (WarpGroup) l).toList(); getMenu().getSlot(ALTERNATE_PLUS_SLOT).setItem(MenuItems.ITEM_BACKGROUND); + final int nextSlot = recalculateAutoSlots(warpGroups); // Create the country items - int slot = 0; - for (WarpGroup warpGroup : warpGroups) { // Create a create warp group item if the player has permission - if(isPlusItem(warpGroup) && slot == warpGroups.size() - 1){ - getMenu().getSlot(getPlusSlot(slot)).setItem( + if(isPlusItem(warpGroup)){ + setPlusSlot(warpGroup.getInternalSlot(), nextSlot); + getMenu().getSlot(plusSlot).setItem( Item.createCustomHeadBase64( CustomHeads.GREEN_PLUS, "§a§lCreate a new Warp Group", ListUtil.createList("§8Click to create a new warp group.") ) ); - slot++; continue; } - getMenu().getSlot(getWarpGroupSlot(warpGroup, slot)).setItem(warpGroup.getMaterialItem()); - slot++; + getMenu().getSlot(getWarpGroupSlot(warpGroup)).setItem(warpGroup.getMaterialItem()); } } @Override protected void setPaginatedMenuItemsAsync(List source) { - + // All Items set above in setPaginatedPreviewItems } @Override @@ -99,69 +111,139 @@ protected Mask getMask() { @Override protected List getSource() { // Get the warp groups in the build team sorted by name - List warpGroups = buildTeam.getWarpGroups().stream().sorted((warpGroup1, warpGroup2) -> warpGroup1.getName().compareToIgnoreCase(warpGroup2.getName())).collect(Collectors.toList()); + List warpGroups; + + String mode = BuildTeamTools.getInstance().getConfig(ConfigUtil.NAVIGATION) + .getString(ConfigPaths.Navigation.WARPS_GROUP_SORTING_MODE, ""); + + if (mode.equalsIgnoreCase("name")) { + warpGroups = buildTeam.getWarpGroups().stream().sorted((warpGroup1, warpGroup2) -> warpGroup1.getName().compareToIgnoreCase(warpGroup2.getName())).collect(Collectors.toList()); + } else warpGroups = new ArrayList<>(buildTeam.getWarpGroups()); // If the warp group "Other" has no warps, remove it from the list WarpGroup otherWarpGroup = warpGroups.stream().filter(warpGroup -> warpGroup.getName().equalsIgnoreCase("Other")).findFirst().orElse(null); - if(otherWarpGroup != null && otherWarpGroup.getWarps().size() == 0) + if (otherWarpGroup != null && otherWarpGroup.getWarps().isEmpty()) warpGroups.remove(otherWarpGroup); // Add a create warp group item if the player has permission - if (getMenuPlayer().hasPermission(Permissions.WARP_GROUP_CREATE)) + if (getMenuPlayer().hasPermission(Permissions.WARP_GROUP_CREATE) + && NetworkModule.getInstance().getBuildTeam().equals(buildTeam)) warpGroups.add(new WarpGroup(null, "%create-warp-group%", null, -1, null)); - return warpGroups; } @Override - protected void setPaginatedItemClickEventsAsync(List source) { - List warpGroups = source.stream().map(l -> (WarpGroup) l).collect(Collectors.toList()); + protected void setPaginatedItemClickEventsAsync(@NotNull List source) { + List warpGroups = source.stream().map(l -> (WarpGroup) l).toList(); - int slot = 0; for (WarpGroup warpGroup : warpGroups) { + setClickEventForSlot(warpGroup); + } + } - final int _slot = isPlusItem(warpGroup) ? getPlusSlot(slot) : getWarpGroupSlot(warpGroup, slot); + protected void setClickEventForSlot(@NotNull WarpGroup warpGroup) { + final int _slot = isPlusItem(warpGroup) ? plusSlot : getWarpGroupSlot(warpGroup); - getMenu().getSlot(_slot).setClickHandler((clickPlayer, clickInformation) -> { - clickPlayer.closeInventory(); + getMenu().getSlot(_slot).setClickHandler((clickPlayer, clickInformation) -> { + clickPlayer.closeInventory(); - // Create a click action for the "Create Warp" item if the player has permission - if(isPlusItem(warpGroup)){ - NavigationModule.getInstance().getWarpsComponent().createWarpGroup(clickPlayer); - return; - } + // Create a click action for the "Create Warp" item if the player has permission + if(isPlusItem(warpGroup)){ + NavigationModule.getInstance().getWarpsComponent().createWarpGroup(clickPlayer); + return; + } - if(clickInformation.getClickType().isRightClick() && clickPlayer.hasPermission(Permissions.WARP_GROUP_EDIT)) - new WarpGroupEditMenu(clickPlayer, warpGroup, true, true); - else - new WarpMenu(clickPlayer, warpGroup, true, true); + if(clickInformation.getClickType().isRightClick() && clickPlayer.hasPermission(Permissions.WARP_GROUP_EDIT)) + new WarpGroupEditMenu(clickPlayer, warpGroup, true, true); + else + leftClickAction(clickPlayer, warpGroup); + }); + } - }); - slot++; - } + protected void leftClickAction(Player clickPlayer, @NotNull WarpGroup warpGroup) { + new WarpMenu(clickPlayer, warpGroup, true, true); } - protected boolean isPlusItem(WarpGroup warpGroup){ + protected boolean isPlusItem(@NotNull WarpGroup warpGroup){ return warpGroup.getName().equals("%create-warp-group%") && getMenuPlayer().hasPermission(Permissions.WARP_GROUP_CREATE); } - protected int getWarpGroupSlot(WarpGroup warpGroup, int currentIndex){ - int warpGroupSlot = currentIndex; + protected int getWarpGroupSlot(@NotNull WarpGroup g) { + int s = g.getSlot(); + if (s >= 0 && s <= 26) return s; + int a = g.getInternalSlot(); + return a >= 0 && a <= 26 ? a : -1; + } - if(warpGroup.getSlot() != -1 && warpGroup.getSlot() > 0 && warpGroup.getSlot() < 27) - warpGroupSlot = warpGroup.getSlot(); + protected void setPlusSlot(int internalSlot, int freeSlot){ + if(internalSlot >= 0) { + plusSlot = internalSlot; + return; + } - return warpGroupSlot; + if(freeSlot < 0) return; + plusSlot = freeSlot; } - protected int getPlusSlot(int currentIndex){ - int warpGroupSlot = currentIndex; + /** + * Recalculates automatic slot assignments for the given warp groups. + *

+ * Preserves valid explicit slots (0..26). For groups with an invalid slot, + * assigns the first available slot in ascending order; if none remain, sets -1. + * Any group with a valid explicit slot has its internal auto slot cleared (-1). + * + * @param warpGroups groups to update + * @return The next free slot or -1 if it's outside the range + */ + private static int recalculateAutoSlots(@NotNull List warpGroups) { + final int MAX = 27; + + // 1) Calculate all free slots + ArrayDeque free = getFreeSlots(warpGroups, MAX); + + // 2) Assign auto slots only where needed + for (WarpGroup g : warpGroups) { + int s = g.getSlot(); + if (s >= 0 && s < MAX) { + g.setInternalSlot(-1); // explicit slot → clear auto + } else { + Integer next = free.pollFirst(); // next free, or null if none + g.setInternalSlot(next != null ? next : -1); + } + } + + // 3) Return next free slot - mainly used for plus slot + Integer next = free.pollFirst(); // next free, or null if none - for(WarpGroup warpGroup : buildTeam.getWarpGroups()) - if(warpGroup.getSlot() != -1 && warpGroup.getSlot() > 0 && warpGroup.getSlot() < 27) - warpGroupSlot = ALTERNATE_PLUS_SLOT; + if (BuildTeamTools.getInstance().isDebug() && BuildTeamTools.getInstance().getComponentLogger().isInfoEnabled()) { + BuildTeamTools.getInstance().getComponentLogger().info("Free slot: {}.", free); + BuildTeamTools.getInstance().getComponentLogger().info("Auto slots: {}.", + new Gson().toJson(warpGroups.stream().map(warpGroup -> new WarpGropSlotDebug( + warpGroup.getName(), + warpGroup.getSlot(), + warpGroup.getInternalSlot())).toList())); + } - return warpGroupSlot; + return next != null ? next : -1; } + + private static @NotNull ArrayDeque getFreeSlots(@NotNull List warpGroups, int max) { + // 1) Mark occupied (explicit) slots + boolean[] taken = new boolean[max]; + for (WarpGroup g : warpGroups) { + int s = g.getSlot(); + if (s >= 0 && s < max) taken[s] = true; + } + + // 2) Build the free-slot queue (ascending) + ArrayDeque free = new ArrayDeque<>(); + for (int i = 0; i < max; i++) { + if (!taken[i]) free.add(i); + } + + return free; + } + + private record WarpGropSlotDebug(String name, int slot, int internalSlot) {} } diff --git a/src/main/java/net/buildtheearth/modules/navigation/components/warps/menu/WarpGroupSelectionMenu.java b/src/main/java/net/buildtheearth/modules/navigation/components/warps/menu/WarpGroupSelectionMenu.java index accb5a0b..70df51eb 100644 --- a/src/main/java/net/buildtheearth/modules/navigation/components/warps/menu/WarpGroupSelectionMenu.java +++ b/src/main/java/net/buildtheearth/modules/navigation/components/warps/menu/WarpGroupSelectionMenu.java @@ -5,9 +5,7 @@ import net.buildtheearth.modules.navigation.components.warps.model.WarpGroup; import org.bukkit.Sound; import org.bukkit.entity.Player; - -import java.util.List; -import java.util.stream.Collectors; +import org.jetbrains.annotations.NotNull; public class WarpGroupSelectionMenu extends WarpGroupMenu { @@ -38,20 +36,8 @@ protected void setItemClickEventsAsync() { } @Override - protected void setPaginatedItemClickEventsAsync(List source) { - List warpGroups = source.stream().map(l -> (WarpGroup) l).collect(Collectors.toList()); - - int slot = 0; - for (WarpGroup warpGroup : warpGroups) { - final int _slot = slot; - getMenu().getSlot(getWarpGroupSlot(warpGroup, _slot)).setClickHandler((clickPlayer, clickInformation) -> { - clickPlayer.closeInventory(); - clickPlayer.playSound(clickPlayer.getLocation(), Sound.ENTITY_PLAYER_LEVELUP, 1.0F, 1.0F); - - warp.setWarpGroup(warpGroup); - new WarpEditMenu(clickPlayer, warp, alreadyExists, true); - }); - slot++; - } - } + protected void leftClickAction(@NotNull Player clickPlayer, @NotNull WarpGroup warpGroup) { + clickPlayer.playSound(clickPlayer.getLocation(), Sound.ENTITY_PLAYER_LEVELUP, 1.0F, 1.0F); + warp.setWarpGroup(warpGroup); + new WarpEditMenu(clickPlayer, warp, alreadyExists, true); } } diff --git a/src/main/java/net/buildtheearth/modules/navigation/components/warps/menu/WarpMenu.java b/src/main/java/net/buildtheearth/modules/navigation/components/warps/menu/WarpMenu.java index c333f716..d80c4438 100644 --- a/src/main/java/net/buildtheearth/modules/navigation/components/warps/menu/WarpMenu.java +++ b/src/main/java/net/buildtheearth/modules/navigation/components/warps/menu/WarpMenu.java @@ -1,23 +1,26 @@ package net.buildtheearth.modules.navigation.components.warps.menu; import net.buildtheearth.modules.navigation.NavigationModule; -import net.buildtheearth.modules.network.model.Permissions; -import net.buildtheearth.utils.*; -import net.buildtheearth.utils.menus.AbstractPaginatedMenu; import net.buildtheearth.modules.navigation.components.warps.model.Warp; import net.buildtheearth.modules.navigation.components.warps.model.WarpGroup; +import net.buildtheearth.modules.network.NetworkModule; +import net.buildtheearth.modules.network.model.Permissions; +import net.buildtheearth.utils.CustomHeads; +import net.buildtheearth.utils.Item; +import net.buildtheearth.utils.ListUtil; +import net.buildtheearth.utils.MenuItems; +import net.buildtheearth.utils.menus.AbstractPaginatedMenu; import org.bukkit.entity.Player; import org.ipvp.canvas.mask.BinaryMask; import org.ipvp.canvas.mask.Mask; +import org.jetbrains.annotations.NotNull; -import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; public class WarpMenu extends AbstractPaginatedMenu { - public final int BACK_ITEM_SLOT = 27; + public static final int BACK_ITEM_SLOT = 27; private final boolean hasBackItem; private final WarpGroup warpGroup; @@ -42,8 +45,7 @@ protected void setMenuItemsAsync() { } @Override - protected void setItemClickEventsAsync() { - } + protected void setItemClickEventsAsync() { /* Not needed */ } @Override protected Mask getMask() { @@ -62,15 +64,16 @@ protected List getSource() { List warps = warpGroup.getWarps().stream().sorted((warp1, warp2) -> warp1.getName().compareToIgnoreCase(warp2.getName())).collect(Collectors.toList()); // Add a "create warp" item if the player has permission - if (getMenuPlayer().hasPermission(Permissions.WARP_CREATE)) + if (getMenuPlayer().hasPermission(Permissions.WARP_CREATE) + && NetworkModule.getInstance().getBuildTeam().equals(warpGroup.getBuildTeam())) warps.add(new Warp(null, "%create-warp%", null, null, null, null, null, null, 0, 0, 0, 0, 0, false)); return warps; } @Override - protected void setPaginatedPreviewItems(List source) { - List warps = source.stream().map(l -> (Warp) l).collect(Collectors.toList()); + protected void setPaginatedPreviewItems(@NotNull List source) { + List warps = source.stream().map(l -> (Warp) l).toList(); // Create the country items int slot = 0; @@ -84,10 +87,6 @@ protected void setPaginatedPreviewItems(List source) { continue; } - ArrayList loreLines = ListUtil.createList("", "§eAddress:"); - loreLines.addAll(Arrays.asList(Utils.splitStringByLineLength(warp.getAddress(), 30, ", "))); - loreLines.addAll(ListUtil.createList("", "§8Left-Click to warp to this location.", "§8Right-Click to edit this warp.")); - getMenu().getSlot(slot).setItem(warp.getMaterialItem()); slot++; } @@ -96,13 +95,11 @@ protected void setPaginatedPreviewItems(List source) { } @Override - protected void setPaginatedMenuItemsAsync(List source) { - - } + protected void setPaginatedMenuItemsAsync(List source) {/* Not needed */} @Override - protected void setPaginatedItemClickEventsAsync(List source) { - List warps = source.stream().map(l -> (Warp) l).collect(Collectors.toList()); + protected void setPaginatedItemClickEventsAsync(@NotNull List source) { + List warps = source.stream().map(l -> (Warp) l).toList(); int slot = 0; for (Warp warp : warps) { @@ -112,11 +109,12 @@ protected void setPaginatedItemClickEventsAsync(List source) { // Create a click action for the "Create Warp" item if the player has permission if(warp.getName().equals("%create-warp%") && getMenuPlayer().hasPermission(Permissions.WARP_CREATE) && _slot == warps.size() - 1){ - NavigationModule.getInstance().getWarpsComponent().createWarp(clickPlayer); + NavigationModule.getInstance().getWarpsComponent().createWarp(clickPlayer, warpGroup); return; } - if(clickInformation.getClickType().isRightClick() && clickPlayer.hasPermission(Permissions.WARP_EDIT)) + if (clickInformation.getClickType().isRightClick() && clickPlayer.hasPermission(Permissions.WARP_EDIT) + && warp.getWarpGroup().getBuildTeam().equals(NetworkModule.getInstance().getBuildTeam())) new WarpEditMenu(clickPlayer, warp, true, true); else NavigationModule.getInstance().getWarpsComponent().warpPlayer(clickPlayer, warp); diff --git a/src/main/java/net/buildtheearth/modules/navigation/components/warps/model/WarpGroup.java b/src/main/java/net/buildtheearth/modules/navigation/components/warps/model/WarpGroup.java index 533160c0..e51282e8 100644 --- a/src/main/java/net/buildtheearth/modules/navigation/components/warps/model/WarpGroup.java +++ b/src/main/java/net/buildtheearth/modules/navigation/components/warps/model/WarpGroup.java @@ -3,7 +3,9 @@ import lombok.Getter; import lombok.Setter; import net.buildtheearth.modules.network.model.BuildTeam; -import net.buildtheearth.utils.*; +import net.buildtheearth.utils.CustomHeads; +import net.buildtheearth.utils.Item; +import net.buildtheearth.utils.ListUtil; import org.bukkit.Material; import org.bukkit.inventory.ItemStack; import org.json.JSONObject; @@ -18,7 +20,7 @@ public class WarpGroup { private UUID id = UUID.randomUUID(); @Getter - private BuildTeam buildTeam; + private final BuildTeam buildTeam; @Getter @Setter private String name; @@ -35,6 +37,9 @@ public class WarpGroup { @Getter private final List warps; + // Slot which is used internally by the navigation module when auto slot is enabled + @Getter @Setter + private int internalSlot = -1; public WarpGroup(BuildTeam buildTeam, String name, String description, int slot, String material) { this.buildTeam = buildTeam; @@ -81,9 +86,9 @@ public ItemStack getMaterialItem() { else if(material.startsWith("http://textures.minecraft.net/texture/")) return Item.createCustomHeadTextureURL(material, itemName, lore); - Material material = Material.matchMaterial(this.material.split(":")[0]); + Material matchedMaterial = Material.matchMaterial(this.material.split(":")[0]); - if(material == null) + if(matchedMaterial == null) return CustomHeads.getLetterHead( name.substring(0, 1), CustomHeads.LetterType.STONE, @@ -91,9 +96,9 @@ else if(material.startsWith("http://textures.minecraft.net/texture/")) lore ); else if(!this.material.contains(":")) - return Item.create(material, itemName, lore); + return Item.create(matchedMaterial, itemName, lore); else - return Item.create(material, itemName, Short.parseShort(this.material.split(":")[1]), lore); + return Item.create(matchedMaterial, itemName, Short.parseShort(this.material.split(":")[1]), lore); } diff --git a/src/main/java/net/buildtheearth/modules/navigation/menu/CountrySelectorMenu.java b/src/main/java/net/buildtheearth/modules/navigation/menu/CountrySelectorMenu.java index 3678e3d2..aafb89d1 100644 --- a/src/main/java/net/buildtheearth/modules/navigation/menu/CountrySelectorMenu.java +++ b/src/main/java/net/buildtheearth/modules/navigation/menu/CountrySelectorMenu.java @@ -1,32 +1,36 @@ package net.buildtheearth.modules.navigation.menu; import lombok.NonNull; +import net.buildtheearth.modules.navigation.NavUtils; +import net.buildtheearth.modules.navigation.components.warps.WarpsComponent; import net.buildtheearth.modules.network.NetworkModule; import net.buildtheearth.modules.network.model.BuildTeam; import net.buildtheearth.modules.network.model.Continent; +import net.buildtheearth.modules.network.model.Permissions; import net.buildtheearth.modules.network.model.Region; -import net.buildtheearth.utils.*; +import net.buildtheearth.utils.ChatHelper; +import net.buildtheearth.utils.Item; +import net.buildtheearth.utils.ListUtil; +import net.buildtheearth.utils.MenuItems; import net.buildtheearth.utils.menus.AbstractPaginatedMenu; import org.bukkit.entity.Player; import org.ipvp.canvas.mask.BinaryMask; import org.ipvp.canvas.mask.Mask; +import org.jetbrains.annotations.NotNull; import java.util.ArrayList; import java.util.Comparator; import java.util.List; -import java.util.stream.Collectors; public class CountrySelectorMenu extends AbstractPaginatedMenu { - private final Continent continent; private final List regions; - public final int BACK_ITEM_SLOT = 27; - public static int SWITCH_PAGE_ITEM_SLOT = 34; + public static final int BACK_ITEM_SLOT = 27; + public static final int SWITCH_PAGE_ITEM_SLOT = 34; public CountrySelectorMenu(Player menuPlayer, @NonNull Continent continent, boolean autoLoad) { super(4, 3, continent.getLabel() + " - countries", menuPlayer, autoLoad); - this.continent = continent; this.regions = new ArrayList<>(continent.getCountries()); // Add USA region to North America because it is being built by multiple teams @@ -34,13 +38,13 @@ public CountrySelectorMenu(Player menuPlayer, @NonNull Continent continent, bool regions.add( new Region("USA", Continent.NORTH_AMERICA, - new BuildTeam(null, null, null, "4 Teams", null, Continent.NORTH_AMERICA, false, false), + new BuildTeam(null, null, null, "4 Teams", null, false, false, false, ""), "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvNGNhYzk3NzRkYTEyMTcyNDg1MzJjZTE0N2Y3ODMxZjY3YTEyZmRjY2ExY2YwY2I0YjM4NDhkZTZiYzk0YjQifX19" , 9372610, "US", "USA" ) ); - if(regions.size() > 0) { + if (!regions.isEmpty()) { // Sort countries by area regions.sort(Comparator.comparing(Region::getArea).reversed()); @@ -48,13 +52,12 @@ public CountrySelectorMenu(Player menuPlayer, @NonNull Continent continent, bool regions.removeAll(regions.stream().filter(region -> region.getBuildTeam() == null || region.getHeadBase64() == null - || region.getBuildTeam() == null || region.getBuildTeam().getID() == null || ( NetworkModule.getInstance().getBuildTeam() != null && region.getBuildTeam().getID().equals(NetworkModule.getInstance().getBuildTeam().getID()) ) - ).collect(Collectors.toList())); + ).toList()); } ChatHelper.logDebug("Continent in constructor: %s", continent); @@ -75,14 +78,17 @@ protected void setPreviewItems() { } @Override - protected void setPaginatedPreviewItems(List source) { - List countries = source.stream().map(l -> (Region) l).collect(Collectors.toList()); + protected void setPaginatedPreviewItems(@NotNull List source) { + List countries = source.stream().map(l -> (Region) l).toList(); // Create the country items int slot = 0; for (Region region : countries) { ArrayList countryLore = ListUtil.createList("", "§eBuild Team:", region.getBuildTeam().getBlankName(), "", "§eArea:", formatArea(region.getArea()) + " km²", "", "§8Click to join this country's server!"); + if (!region.getBuildTeam().getWarpGroups().isEmpty()) { + countryLore.add("Right click to open the warp group menu!"); + } getMenu().getSlot(slot).setItem( Item.createCustomHeadBase64(region.getHeadBase64() == null ? "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYmFkYzA0OGE3Y2U3OGY3ZGFkNzJhMDdkYTI3ZDg1YzA5MTY4ODFlNTUyMmVlZWQxZTNkYWYyMTdhMzhjMWEifX19" : region.getHeadBase64(), "§6§l" + region.getName(), @@ -93,7 +99,8 @@ protected void setPaginatedPreviewItems(List source) { } @Override - protected void setMenuItemsAsync() {} + protected void setMenuItemsAsync(/* No async Items set */) { + } @Override protected void setItemClickEventsAsync() { @@ -102,8 +109,8 @@ protected void setItemClickEventsAsync() { } @Override - protected void setPaginatedItemClickEventsAsync(List source) { - List countries = source.stream().map(l -> (Region) l).collect(Collectors.toList()); + protected void setPaginatedItemClickEventsAsync(@NotNull List source) { + List countries = source.stream().map(l -> (Region) l).toList(); int slot = 0; for (Region clickedRegion : countries) { @@ -111,20 +118,20 @@ protected void setPaginatedItemClickEventsAsync(List source) { getMenu().getSlot(_slot).setClickHandler((clickPlayer, clickInformation) -> { clickPlayer.closeInventory(); - ChatHelper.logDebug("%s", clickedRegion.getName()); + ChatHelper.logDebug("Clicked Region: %s", clickedRegion.getName() + " (" + clickedRegion.getCountryCodeCca3() + ")"); - if(clickedRegion.getCountryCodeCca3().equalsIgnoreCase("USA")) - new StateSelectorMenu(clickedRegion, clickPlayer, true); - else if (clickedRegion.getBuildTeam().isConnected()) - Utils.sendPlayerToServer(clickPlayer, clickedRegion.getBuildTeam().getServerName()); - else - NetworkModule.sendNotConnectedMessage(clickPlayer, clickedRegion.getBuildTeam().getIP()); + if (clickInformation.getClickType().isRightClick() && + clickPlayer.hasPermission(Permissions.WARP_USE) && + !clickedRegion.getBuildTeam().getWarpGroups().isEmpty()) { + WarpsComponent.openWarpMenu(clickPlayer, clickedRegion.getBuildTeam(), this); + } else { + NavUtils.switchToTeam(clickedRegion.getBuildTeam(), clickPlayer); + } }); slot++; } } - @Override protected Mask getMask() { return BinaryMask.builder(getMenu()) @@ -146,10 +153,8 @@ protected void setPaginatedMenuItemsAsync(List source) { } - /** Converts an area in square meters to a string with dot notation starting from the right every 3 digits. - * - * @param area - * @return + /** + * Converts an area in square meters to a string with dot notation starting from the right every 3 digits. */ public static String formatArea(double area) { String areaStr = String.valueOf((int) area); diff --git a/src/main/java/net/buildtheearth/modules/navigation/menu/ExploreMenu.java b/src/main/java/net/buildtheearth/modules/navigation/menu/ExploreMenu.java index 231e8442..ba3a6a97 100644 --- a/src/main/java/net/buildtheearth/modules/navigation/menu/ExploreMenu.java +++ b/src/main/java/net/buildtheearth/modules/navigation/menu/ExploreMenu.java @@ -7,7 +7,6 @@ import net.buildtheearth.utils.Item; import net.buildtheearth.utils.MenuItems; import net.buildtheearth.utils.menus.AbstractMenu; -import net.kyori.adventure.text.format.NamedTextColor; import org.bukkit.entity.Player; import org.ipvp.canvas.mask.BinaryMask; import org.ipvp.canvas.mask.Mask; @@ -34,8 +33,8 @@ public ExploreMenu(Player menuPlayer, boolean autoLoad) { protected void setPreviewItems() { // Create the continent items for (Continent continent : Continent.values()) { - ArrayList continentLore = new ArrayList<>(Collections.singletonList(ChatHelper.getStandardString("Visit countries in %s", continent.getLabel()))); - getMenu().getSlot(continent.getSlot()).setItem(Item.create(XMaterial.COMPASS.parseMaterial(),"§e§l" + continent.getLabel(), 1, continentLore)); + ArrayList continentLore = new ArrayList<>(Collections.singletonList(ChatHelper.getStandardString(false, "Visit countries in %s", continent.getLabel()))); + getMenu().getSlot(continent.getSlot()).setItem(Item.create(XMaterial.COMPASS.get(), "§e§l" + continent.getLabel(), 1, continentLore)); } super.setPreviewItems(); @@ -48,13 +47,9 @@ protected void setItemClickEventsAsync() { getMenu().getSlot(continent.getSlot()).setClickHandler((clickPlayer, clickInformation) -> { clickPlayer.closeInventory(); - System.out.println("Continent before creating CountrySelectorMenu: " + continent); // Add this line + ChatHelper.logDebug("Clicked Continent before creating CountrySelectorMenu: %s", continent.getLabel()); - if(continent.equals(Continent.AFRICA)) { - // TODO implement that the player gets information about the BTE Africa server when clicking on Africa - } else { - new CountrySelectorMenu(clickPlayer, continent, true); - } + new CountrySelectorMenu(clickPlayer, continent, true); }); } } diff --git a/src/main/java/net/buildtheearth/modules/navigation/menu/MainMenu.java b/src/main/java/net/buildtheearth/modules/navigation/menu/MainMenu.java index d826cc68..1d9b7dbc 100644 --- a/src/main/java/net/buildtheearth/modules/navigation/menu/MainMenu.java +++ b/src/main/java/net/buildtheearth/modules/navigation/menu/MainMenu.java @@ -2,21 +2,29 @@ import com.cryptomorin.xseries.XMaterial; import net.buildtheearth.BuildTeamTools; +import net.buildtheearth.modules.navigation.NavUtils; +import net.buildtheearth.modules.navigation.components.warps.WarpsComponent; import net.buildtheearth.modules.network.NetworkModule; import net.buildtheearth.utils.ChatHelper; import net.buildtheearth.utils.Item; import net.buildtheearth.utils.MenuItems; -import net.buildtheearth.utils.Utils; import net.buildtheearth.utils.io.ConfigPaths; +import net.buildtheearth.utils.io.ConfigUtil; import net.buildtheearth.utils.menus.AbstractMenu; import net.kyori.adventure.text.format.NamedTextColor; +import org.apache.commons.lang3.BooleanUtils; import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.entity.Player; import org.ipvp.canvas.mask.BinaryMask; import org.ipvp.canvas.mask.Mask; +import org.jetbrains.annotations.NotNull; +import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Collections; +import java.util.Deque; +import java.util.List; +import java.util.Objects; /** * The main menu for the BTE universal navigator.
@@ -31,86 +39,102 @@ */ public class MainMenu extends AbstractMenu { - private static final String inventoryName = "BuildTheEarth Navigator"; + private static final String INVENTORY_NAME = "BuildTheEarth Navigator"; private static FileConfiguration config; public MainMenu(Player menuPlayer) { - super(3, inventoryName, menuPlayer); + super(3, INVENTORY_NAME, menuPlayer); } @Override protected void setPreviewItems() { - config = BuildTeamTools.getInstance().getConfig(); - int[] slots = getSlots(); + config = BuildTeamTools.getInstance().getConfig(ConfigUtil.NAVIGATION); + @NotNull Deque<@NotNull Integer> slots = getSlots(); // Fill the blank slots with glass panes - getMenu().getSlot(11).setItem(MenuItems.ITEM_BACKGROUND); - getMenu().getSlot(13).setItem(MenuItems.ITEM_BACKGROUND); - getMenu().getSlot(15).setItem(MenuItems.ITEM_BACKGROUND); - - // Set Explore Item - ArrayList exploreLore = new ArrayList<>(Collections.singletonList(ChatHelper.getColorizedString(NamedTextColor.GRAY, "Click to explore the project!", false))); - getMenu().getSlot(slots[0]).setItem(Item.create(XMaterial.SPRUCE_BOAT.parseMaterial(), ChatHelper.getColorizedString(NamedTextColor.YELLOW, "Explore", true), 1, exploreLore)); - + for (int i = 10; i <= 16; i++) { + getMenu().getSlot(i).setItem(MenuItems.ITEM_BACKGROUND); + } // Set Build Item - if(config.getBoolean(ConfigPaths.Navigation.BUILD_ITEM_ENABLED)) { + if (config.getBoolean(ConfigPaths.Navigation.BUILD_ITEM_ENABLED)) { ArrayList buildLore = new ArrayList<>(Collections.singletonList(ChatHelper.getColorizedString(NamedTextColor.GRAY, "Click to build for the project!", false))); - getMenu().getSlot(slots[1]).setItem(Item.create(XMaterial.DIAMOND_PICKAXE.parseMaterial(), ChatHelper.getColorizedString(NamedTextColor.GREEN, "Build", true), 1, buildLore)); + getMenu().getSlot(Objects.requireNonNull(slots.pollFirst())).setItem(Item.edit(Objects.requireNonNull(XMaterial.DIAMOND_PICKAXE.parseItem()), 1, ChatHelper.getColorizedString(NamedTextColor.GREEN, "Terra Server", true), buildLore)); + } + + // Set Plotsystem Item Click Event + if (config.getBoolean(ConfigPaths.Navigation.PLOTSYSTEM_ITEM_ENABLED)) { + ArrayList tutorialsLore = new ArrayList<>(Collections.singletonList(ChatHelper.getColorizedString(NamedTextColor.GRAY, "Click to start your journey!", false))); + getMenu().getSlot(Objects.requireNonNull(slots.pollFirst())).setItem(Item.edit(Objects.requireNonNull(XMaterial.KNOWLEDGE_BOOK.parseItem()), 1, ChatHelper.getColorizedString(NamedTextColor.AQUA, "Plot System", true), tutorialsLore)); + } + + if (config.getBoolean(ConfigPaths.Navigation.EXPLORE_ITEM_ENABLED)) { + // Set Explore Item + List exploreLore = List.of(ChatHelper.getColorizedString(NamedTextColor.GRAY, "Click to explore the warps!", false), ChatHelper.getColorizedString(NamedTextColor.LIGHT_PURPLE, "Right click to explore other build teams.", false)); + getMenu().getSlot(Objects.requireNonNull(slots.pollFirst())).setItem(Item.edit(Objects.requireNonNull(XMaterial.SPRUCE_BOAT.parseItem()), 1, ChatHelper.getColorizedString(NamedTextColor.YELLOW, "Explore", true), exploreLore)); } // Set Tutorials Item - if(config.getBoolean(ConfigPaths.Navigation.TUTORIALS_ITEM_ENABLED)) { + if (config.getBoolean(ConfigPaths.Navigation.TUTORIALS_ITEM_ENABLED)) { ArrayList tutorialsLore = new ArrayList<>(Collections.singletonList(ChatHelper.getColorizedString(NamedTextColor.GRAY, "Click to do some tutorials!", false))); - getMenu().getSlot(slots[2]).setItem(Item.create(XMaterial.KNOWLEDGE_BOOK.parseMaterial(), ChatHelper.getColorizedString(NamedTextColor.AQUA, "Tutorials", true), 1, tutorialsLore)); + getMenu().getSlot(Objects.requireNonNull(slots.pollFirst())).setItem(Item.edit(Objects.requireNonNull(XMaterial.KNOWLEDGE_BOOK.parseItem()), 1, ChatHelper.getColorizedString(NamedTextColor.AQUA, "Tutorials", true), tutorialsLore)); } super.setPreviewItems(); } @Override - protected void setMenuItemsAsync() {} + protected void setMenuItemsAsync() { /* No async Items set */} @Override protected void setItemClickEventsAsync() { - int[] slots = getSlots(); - - // Set Explore Item Click Event - getMenu().getSlot(slots[0]).setClickHandler((clickPlayer, clickInformation) -> { - clickPlayer.closeInventory(); - new ExploreMenu(clickPlayer, true); - }); + Deque slots = getSlots(); // Set Build Item Click Event - if(config.getBoolean(ConfigPaths.Navigation.BUILD_ITEM_ENABLED)) { - getMenu().getSlot(slots[1]).setClickHandler((clickPlayer, clickInformation) -> { - clickPlayer.closeInventory(); - String action = config.getString(ConfigPaths.Navigation.BUILD_ITEM_ACTION); + if (config.getBoolean(ConfigPaths.Navigation.BUILD_ITEM_ENABLED)) { + getMenu().getSlot(Objects.requireNonNull(slots.pollFirst())) + .setClickHandler((clickPlayer, clickInformation) -> { + clickPlayer.closeInventory(); + String action = config.getString(ConfigPaths.Navigation.BUILD_ITEM_ACTION); + performClickAction(clickPlayer, Objects.requireNonNull(action).replace("&", "§"), "build"); + }); + } - // If no command or message is set, teleport player to the plot system server - if(action == null || action.equals("/command") || action.equals("message")) { - clickPlayer.sendMessage(ChatHelper.getStandardString("Teleporting you to the plot system server...")); - Utils.sendPlayerToServer(clickPlayer, NetworkModule.GLOBAL_PLOT_SYSTEM_SERVER); - return; - } + // Set Plotsystem Item Click Event + if (config.getBoolean(ConfigPaths.Navigation.PLOTSYSTEM_ITEM_ENABLED)) { + getMenu().getSlot(Objects.requireNonNull(slots.pollFirst())) + .setClickHandler((clickPlayer, clickInformation) -> { + clickPlayer.closeInventory(); + String action = config.getString(ConfigPaths.Navigation.PLOTSYSTEM_ITEM_ACTION); + performClickAction(clickPlayer, Objects.requireNonNull(action).replace("&", "§"), "plotsystem"); + }); + } - performClickAction(clickPlayer, action.replace("&", "§")); + if (config.getBoolean(ConfigPaths.Navigation.EXPLORE_ITEM_ENABLED)) { + // Set Explore Item Click Event + getMenu().getSlot(Objects.requireNonNull(slots.pollFirst())).setClickHandler((clickPlayer, clickInformation) -> { + clickPlayer.closeInventory(); + if (clickInformation.getClickType().isRightClick()) { + new ExploreMenu(clickPlayer, true); + } else { + WarpsComponent.openWarpMenu(clickPlayer, NetworkModule.getInstance().getBuildTeam(), this); + } }); } // Set Tutorials Item Click Event - if(config.getBoolean(ConfigPaths.Navigation.TUTORIALS_ITEM_ENABLED)) { - getMenu().getSlot(slots[2]).setClickHandler((clickPlayer, clickInformation) -> { + if (config.getBoolean(ConfigPaths.Navigation.TUTORIALS_ITEM_ENABLED)) { + getMenu().getSlot(Objects.requireNonNull(slots.pollFirst())).setClickHandler((clickPlayer, clickInformation) -> { clickPlayer.closeInventory(); String action = config.getString(ConfigPaths.Navigation.TUTORIALS_ITEM_ACTION); // If no command or message is set, open the tutorial menu - if(action == null || action.equals("/command") || action.equals("message")) { + if (action == null || action.equals("/command") || action.equals("message")) { new TutorialsMenu(clickPlayer); return; } - performClickAction(clickPlayer, action.replace("&", "§")); + performClickAction(clickPlayer, action.replace("&", "§"), "tutorial"); }); } } @@ -120,47 +144,63 @@ protected Mask getMask() { return BinaryMask.builder(getMenu()) .item(MenuItems.ITEM_BACKGROUND) .pattern("111111111") - .pattern("110101011") + .pattern("100000001") .pattern("111111111") .build(); } - /** Returns the slots for the Build, Explore and Tutorials items depending on which items are enabled in the config + /** + * Returns the slots for the Build, Explore and Tutorials items depending on which items are enabled in the config * - * @return int[] - Slots of the Explore [0], Build [1] and Tutorials [2] items + * @return int[] - Slots of the enabled items */ - private int[] getSlots() { - int[] slots = new int[3]; - + private @NotNull Deque<@NotNull Integer> getSlots() { + Deque slots = new ArrayDeque<>(); boolean buildEnabled = config.getBoolean(ConfigPaths.Navigation.BUILD_ITEM_ENABLED); boolean tutorialsEnabled = config.getBoolean(ConfigPaths.Navigation.TUTORIALS_ITEM_ENABLED); + boolean plotsystemEnabled = config.getBoolean(ConfigPaths.Navigation.PLOTSYSTEM_ITEM_ENABLED); + boolean exploreEnabled = config.getBoolean(ConfigPaths.Navigation.EXPLORE_ITEM_ENABLED); - int exploreSlot = 11; - int buildSlot = 13; - int tutorialsSlot = 15; - - int enabledItemCount = (buildEnabled ? 1 : 0) + 1 + (tutorialsEnabled ? 1 : 0); + int enabledItemCount = BooleanUtils.toInteger(buildEnabled) + BooleanUtils.toInteger(tutorialsEnabled) + + BooleanUtils.toInteger(plotsystemEnabled) + BooleanUtils.toInteger(exploreEnabled); // Depending on how many items are enabled, set the slots to the correct positions - if (enabledItemCount == 2) { - if (buildEnabled) - buildSlot = 15; - else - buildSlot = 11; + switch (enabledItemCount) { + case 1: + slots.add(13); + break; + case 2: + slots.add(11); + slots.add(15); + break; + case 3: + slots.add(11); + slots.add(13); + slots.add(15); + break; + case 4: + slots.add(10); + slots.add(12); + slots.add(14); + slots.add(16); + break; + default: + throw new IllegalStateException("Unexpected enabled items value: " + enabledItemCount); } - slots[0] = exploreSlot; - slots[1] = buildSlot; - slots[2] = tutorialsSlot; - return slots; } - private void performClickAction(Player p, String action) { + private void performClickAction(Player p, @NotNull String action, @NotNull String type) { // Check if an action is set in the config - if(!action.equals("/command") &&! action.equals("message")) + if (action.startsWith("transfer:")) { + NavUtils.transferPlayer(p, action.substring(9)); + } else if (action.startsWith("switch:")) { + NavUtils.sendPlayerToConnectedServer(p, action.substring(7)); + } else if (!action.equals("/command") && !action.equals("message")) { p.chat(action); - else - p.sendMessage(ChatHelper.getErrorString("No action is set for the %s in the config yet! Please contact an %s.", "build item", "admin")); + } else { + p.sendMessage(ChatHelper.getErrorString("No action is set for the %s in the config yet! Please contact an %s.", type + " item", "admin")); + } } } \ No newline at end of file diff --git a/src/main/java/net/buildtheearth/modules/navigation/menu/StateSelectorMenu.java b/src/main/java/net/buildtheearth/modules/navigation/menu/StateSelectorMenu.java index 4a348f2c..a239bc8f 100644 --- a/src/main/java/net/buildtheearth/modules/navigation/menu/StateSelectorMenu.java +++ b/src/main/java/net/buildtheearth/modules/navigation/menu/StateSelectorMenu.java @@ -1,14 +1,19 @@ package net.buildtheearth.modules.navigation.menu; import lombok.NonNull; +import net.buildtheearth.modules.navigation.NavUtils; import net.buildtheearth.modules.network.NetworkModule; import net.buildtheearth.modules.network.model.Region; import net.buildtheearth.modules.network.model.RegionType; -import net.buildtheearth.utils.*; +import net.buildtheearth.utils.ChatHelper; +import net.buildtheearth.utils.Item; +import net.buildtheearth.utils.ListUtil; +import net.buildtheearth.utils.MenuItems; import net.buildtheearth.utils.menus.AbstractPaginatedMenu; import org.bukkit.entity.Player; import org.ipvp.canvas.mask.BinaryMask; import org.ipvp.canvas.mask.Mask; +import org.jetbrains.annotations.NotNull; import java.util.ArrayList; import java.util.Comparator; @@ -20,9 +25,10 @@ public class StateSelectorMenu extends AbstractPaginatedMenu { private final Region country; private final List states; - public final int BACK_ITEM_SLOT = 27; - public static int SWITCH_PAGE_ITEM_SLOT = 34; + public static final int BACK_ITEM_SLOT = 27; + public static final int SWITCH_PAGE_ITEM_SLOT = 34; + // TODO Eloborate if this is still needed anywheere, otherwise remove it - maybe ts needed for new jersey public StateSelectorMenu(@NonNull Region country, Player menuPlayer, boolean autoLoad) { super(4, 3, country.getName() + " - states", menuPlayer, autoLoad); this.country = country; @@ -33,14 +39,14 @@ public StateSelectorMenu(@NonNull Region country, Player menuPlayer, boolean aut if(country.getCountryCodeCca3().equalsIgnoreCase("USA")) states.add(NetworkModule.getInstance().getRegions().stream().filter(region -> region.getBuildTeam() != null && region.getBuildTeam().getID().equals("Qy2duN4l")).findFirst().orElse(null)); - if(this.states.size() > 0) { + if (!this.states.isEmpty()) { // Remove all regions that don't have a build team this.states.removeAll(this.states.stream().filter(region -> region == null || region.getBuildTeam() == null || region.getBuildTeam().getID() == null || region.getBuildTeam().getID().equals(NetworkModule.getInstance().getBuildTeam().getID()) - ).collect(Collectors.toList())); + ).toList()); // Sort countries by area this.states.sort(Comparator.comparing(Region::getName)); @@ -63,12 +69,12 @@ protected void setPreviewItems() { @Override protected void setPaginatedPreviewItems(List source) { - List states = source.stream().map(l -> (Region) l).collect(Collectors.toList()); + List collectedStates = source.stream().map(l -> (Region) l).toList(); // Create the country items int slot = 0; - for (Region region : states) { + for (Region region : collectedStates) { ArrayList stateLore = ListUtil.createList("", "§eBuild Team:", region.getBuildTeam().getBlankName(), "", "§8Click to join this state's server!"); getMenu().getSlot(slot).setItem( Item.createCustomHeadBase64(region.getHeadBase64() == null ? "eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYmFkYzA0OGE3Y2U3OGY3ZGFkNzJhMDdkYTI3ZDg1YzA5MTY4ODFlNTUyMmVlZWQxZTNkYWYyMTdhMzhjMWEifX19" : region.getHeadBase64(), @@ -89,8 +95,8 @@ protected void setItemClickEventsAsync() { } @Override - protected void setPaginatedItemClickEventsAsync(List source) { - List countries = source.stream().map(l -> (Region) l).collect(Collectors.toList()); + protected void setPaginatedItemClickEventsAsync(@NotNull List source) { + List countries = source.stream().map(l -> (Region) l).toList(); int slot = 0; for (Region clickedRegion : countries) { @@ -98,12 +104,12 @@ protected void setPaginatedItemClickEventsAsync(List source) { getMenu().getSlot(_slot).setClickHandler((clickPlayer, clickInformation) -> { clickPlayer.closeInventory(); - ChatHelper.logDebug("%s", clickedRegion.getName()); + ChatHelper.logDebug("Clicked the state: %s", clickedRegion.getName() + " (" + clickedRegion.getCountryCodeCca3() + ")"); if (clickedRegion.getBuildTeam().isConnected()) - Utils.sendPlayerToServer(clickPlayer, clickedRegion.getBuildTeam().getServerName()); + NavUtils.sendPlayerToConnectedServer(clickPlayer, clickedRegion.getBuildTeam().getServerName()); else - NetworkModule.sendNotConnectedMessage(clickPlayer, clickedRegion.getBuildTeam().getIP()); + NavUtils.sendNotConnectedMessage(clickPlayer, clickedRegion.getBuildTeam().getIP(), clickedRegion.getBuildTeam().getName()); }); slot++; } diff --git a/src/main/java/net/buildtheearth/modules/navigation/menu/TutorialsMenu.java b/src/main/java/net/buildtheearth/modules/navigation/menu/TutorialsMenu.java index 3303398f..4c4a3c15 100644 --- a/src/main/java/net/buildtheearth/modules/navigation/menu/TutorialsMenu.java +++ b/src/main/java/net/buildtheearth/modules/navigation/menu/TutorialsMenu.java @@ -1,8 +1,6 @@ package net.buildtheearth.modules.navigation.menu; -import net.buildtheearth.BuildTeamTools; import net.buildtheearth.utils.menus.AbstractMenu; -import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.entity.Player; import org.ipvp.canvas.mask.Mask; @@ -16,11 +14,10 @@ public class TutorialsMenu extends AbstractMenu { private static final int BACK_BUTTON_SLOT = 13; - private static final String inventoryName = "Tutorials Menu"; - private static final FileConfiguration config = BuildTeamTools.getInstance().getConfig(); + private static final String INVENTORY_NAME = "Tutorials Menu"; public TutorialsMenu(Player player) { - super(3, inventoryName, player); + super(3, INVENTORY_NAME, player); } @Override diff --git a/src/main/java/net/buildtheearth/modules/network/NetworkModule.java b/src/main/java/net/buildtheearth/modules/network/NetworkModule.java index 2ffae403..355dc8a0 100644 --- a/src/main/java/net/buildtheearth/modules/network/NetworkModule.java +++ b/src/main/java/net/buildtheearth/modules/network/NetworkModule.java @@ -15,12 +15,9 @@ import net.buildtheearth.utils.io.ConfigPaths; import net.buildtheearth.utils.io.Constants; import net.buildtheearth.utils.io.Errors; -import net.md_5.bungee.api.chat.ClickEvent; -import net.md_5.bungee.api.chat.ComponentBuilder; -import net.md_5.bungee.api.chat.HoverEvent; -import net.md_5.bungee.api.chat.TextComponent; import org.bukkit.Bukkit; import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; import java.util.ArrayList; import java.util.List; @@ -31,9 +28,7 @@ public class NetworkModule extends Module { - public static String GLOBAL_PLOT_SYSTEM_SERVER = "NYC-1"; - - public static int CACHE_UPLOAD_SPEED = 20 * 60 * 10 + 20; + public static final int CACHE_UPLOAD_SPEED = 20 * 60 * 10 + 20; /** Information about the build team of this server */ @Getter @Setter @@ -65,8 +60,8 @@ public static NetworkModule getInstance() { @Override public void enable() { - String API_KEY = BuildTeamTools.getInstance().getConfig().getString(ConfigPaths.API_KEY); - if(API_KEY == null || API_KEY.isEmpty() || API_KEY.equals(Constants.DEFAULT_API_KEY)){ + String apiKey = BuildTeamTools.getInstance().getConfig().getString(ConfigPaths.API_KEY); + if (apiKey == null || apiKey.isEmpty() || apiKey.equals(Constants.DEFAULT_API_KEY)) { shutdown(Errors.API_KEY_NOT_CONFIGURED); return; } @@ -106,15 +101,13 @@ public CompletableFuture updateCache() { try { - NetworkAPI.getBuildTeamInformation().thenRun(() -> { - NetworkAPI.setupCurrentServerData() + NetworkAPI.getBuildTeamInformation().thenRun(() -> NetworkAPI.setupCurrentServerData() .thenRun(() -> - future.complete(null)) + future.complete(null)) .exceptionally(e -> { future.completeExceptionally(e); return null; - }); - }).exceptionally(e -> { + })).exceptionally(e -> { future.completeExceptionally(e); return null; }); @@ -146,7 +139,7 @@ public void ping(Player p) { * {@link #ping(Player)} */ public void pingAllOnlinePlayers() { - Bukkit.getOnlinePlayers().forEach(player -> ping(player)); + Bukkit.getOnlinePlayers().forEach(this::ping); } /** @@ -162,36 +155,12 @@ public void switchServer(Player player, String targetServer) { player.sendPluginMessage(BuildTeamTools.getInstance(), "BungeeCord", out.toByteArray()); } - /** Sends a message to the player that the server is not connected to the network yet. - * Instead of the server name the server IP will be displayed so the player can join the other server ip manually. - * - * @param player The player to send the message to - * @param serverIP The IP of the server the player should join - */ - public static void sendNotConnectedMessage(Player player, String serverIP) { - TextComponent comp = new TextComponent("§e" + serverIP + " §7(Click to copy)"); - comp.setClickEvent(new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, serverIP)); - comp.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new ComponentBuilder("§eClick to copy").create())); - - String notConnected = "§cThis server is not connected to the network yet and has a different Server IP:"; - - if(!NetworkModule.getInstance().getBuildTeam().isConnected()) - notConnected = "§cThis server has a different Server IP:"; - - player.closeInventory(); - player.sendMessage(notConnected); - player.sendMessage(""); - player.spigot().sendMessage(comp); - player.sendMessage(""); - player.sendMessage("§cClick on the IP to copy it. Then enter it in your Minecraft Server List. (Paste with CTRL + V)"); - } - /** Returns all regions of the given region type. * * @param regionType The region type to get the regions of * @return A list of all regions of the given region type */ - public static ArrayList getRegionsByRegionType(RegionType regionType){ + public static @NotNull List getRegionsByRegionType(RegionType regionType) { ArrayList regions = new ArrayList<>(); for(Region region : NetworkModule.getInstance().getRegions()) if(region.getType().equals(regionType)) @@ -218,9 +187,9 @@ public boolean ownsRegion(String regionName, String countryCodeCca2) { * @return The BuildTeam with the given ID */ public BuildTeam getBuildTeamByID(String teamID) { - for(BuildTeam buildTeam : buildTeams) - if (buildTeam.getID() != null && buildTeam.getID().equals(teamID)) - return buildTeam; + for (BuildTeam team : buildTeams) + if (team.getID() != null && team.getID().equals(teamID)) + return team; return null; } diff --git a/src/main/java/net/buildtheearth/modules/network/api/NetworkAPI.java b/src/main/java/net/buildtheearth/modules/network/api/NetworkAPI.java index ff137e52..69a8346b 100644 --- a/src/main/java/net/buildtheearth/modules/network/api/NetworkAPI.java +++ b/src/main/java/net/buildtheearth/modules/network/api/NetworkAPI.java @@ -1,6 +1,10 @@ package net.buildtheearth.modules.network.api; +import lombok.experimental.UtilityClass; import net.buildtheearth.BuildTeamTools; +import net.buildtheearth.modules.navigation.NavUtils; +import net.buildtheearth.modules.navigation.components.warps.model.Warp; +import net.buildtheearth.modules.navigation.components.warps.model.WarpGroup; import net.buildtheearth.modules.network.NetworkModule; import net.buildtheearth.modules.network.model.BuildTeam; import net.buildtheearth.modules.network.model.Continent; @@ -8,12 +12,11 @@ import net.buildtheearth.modules.network.model.RegionType; import net.buildtheearth.utils.ChatHelper; import net.buildtheearth.utils.io.ConfigPaths; -import net.buildtheearth.modules.navigation.components.warps.model.Warp; -import net.buildtheearth.modules.navigation.components.warps.model.WarpGroup; import okhttp3.MediaType; import okhttp3.RequestBody; import org.bukkit.Bukkit; import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; import org.json.simple.JSONArray; import org.json.simple.JSONObject; @@ -21,6 +24,7 @@ import java.util.UUID; import java.util.concurrent.CompletableFuture; +@UtilityClass public class NetworkAPI { /** @@ -50,10 +54,10 @@ public void onFailure(IOException e) { } // Add all currently connected regions to their respective continents - public static CompletableFuture getBuildTeamInformation() { + public static @NotNull CompletableFuture getBuildTeamInformation() { CompletableFuture future = new CompletableFuture<>(); - API.getAsync("https://nwapi.buildtheearth.net/api/teams", new API.ApiResponseCallback() { + getAsync("https://nwapi.buildtheearth.net/api/teams", new API.ApiResponseCallback() { @Override public void onResponse(String response) { try { @@ -78,23 +82,12 @@ public void onResponse(String response) { for (Object object : responseArray.toArray()) { // Check if the object is a JSON object - if (!(object instanceof JSONObject)) continue; - JSONObject teamObject = (JSONObject) object; - - // Extract a JSON array containing the regions belonging to the team - Object regions = teamObject.get("Regions"); - if (!(regions instanceof JSONArray)) continue; - JSONArray regionArray = (JSONArray) regions; - - // Extract a JSON array containing the warps belonging to the team - Object warps = teamObject.get("Warps"); - if (!(warps instanceof JSONArray)) continue; - JSONArray warpArray = (JSONArray) warps; - - // Extract a JSON array containing the warp groups belonging to the team - Object warpGroups = teamObject.get("WarpGroups"); - if (!(warpGroups instanceof JSONArray)) continue; - JSONArray warpGroupArray = (JSONArray) warpGroups; + if (!(object instanceof JSONObject teamObject) || + !(teamObject.get("Regions") instanceof JSONArray regions) || + !(teamObject.get("Warps") instanceof JSONArray warps) || + !(teamObject.get("WarpGroups") instanceof JSONArray warpGroups)) { + continue; + } // Get some values to add to the build team Continent continent = Continent.getByLabel((String) teamObject.get("Continent")); @@ -105,18 +98,18 @@ public void onResponse(String response) { String serverName = getMainServerName(teamObject); String name = (String) teamObject.get("Name"); String blankName = (String) teamObject.get("BlankName"); + boolean allowsTransfers = (long) teamObject.get("AllowsTransfers") == 1; + String tag = (String) teamObject.get("Tag"); - BuildTeam buildTeam = new BuildTeam(teamID, mainServerIP, name, blankName, serverName, continent, isConnected, hasBuildTeamToolsInstalled); + BuildTeam buildTeam = new BuildTeam(teamID, mainServerIP, name, blankName, serverName, + isConnected, hasBuildTeamToolsInstalled, allowsTransfers, tag); NetworkModule.getInstance().getBuildTeams().add(buildTeam); - // Create an "other" Warp Group for warps that don't belong to a warp group - WarpGroup otherWarpGroup = new WarpGroup(buildTeam, "Other", "Other warps", -1, null); - buildTeam.getWarpGroups().add(otherWarpGroup); + WarpGroup otherWarpGroup = NavUtils.createOtherWarpGroup(); // Add all the warp groups of the team to their respective build teams - for (Object warpGroupJSON : warpGroupArray.toArray()) { - if (!(warpGroupJSON instanceof JSONObject)) continue; - JSONObject warpGroupObject = (JSONObject) warpGroupJSON; + for (Object warpGroupJSON : warpGroups.toArray()) { + if (!(warpGroupJSON instanceof JSONObject warpGroupObject)) continue; UUID warpGroupID = UUID.fromString((String) warpGroupObject.get("ID")); String warpGroupName = (String) warpGroupObject.get("Name"); @@ -129,10 +122,11 @@ public void onResponse(String response) { buildTeam.getWarpGroups().add(warpGroup); } + if (!otherWarpGroup.getWarps().isEmpty()) buildTeam.getWarpGroups().add(otherWarpGroup); + // Add all the warps of the team to their respective warp groups - for (Object warpJSON : warpArray.toArray()) { - if (!(warpJSON instanceof JSONObject)) continue; - JSONObject warpObject = (JSONObject) warpJSON; + for (Object warpJSON : warps.toArray()) { + if (!(warpJSON instanceof JSONObject warpObject)) continue; String warpIDString = (String) warpObject.get("ID"); UUID warpID = warpIDString != null ? UUID.fromString(warpIDString) : null; @@ -151,7 +145,7 @@ public void onResponse(String response) { float warpPitch = Float.parseFloat(warpObject.get("Pitch") + ""); boolean isHighlight = warpObject.get("isHighlight") != null && (long) warpObject.get("isHighlight") == 1; - if (material != null && material.equals("")) + if (material != null && material.isEmpty()) material = null; WarpGroup warpGroup = null; @@ -186,9 +180,8 @@ public void onResponse(String response) { } // Add all the regions of the team to their respective continents - for (Object regionJSON : regionArray.toArray()) { - if (!(regionJSON instanceof JSONObject)) continue; - JSONObject regionObject = (JSONObject) regionJSON; + for (Object regionJSON : regions.toArray()) { + if (!(regionJSON instanceof JSONObject regionObject)) continue; String regionName = (String) regionObject.get("RegionName"); String headBase64 = (String) regionObject.get("Head"); @@ -226,12 +219,10 @@ private String getMainServerName(JSONObject teamObject) { String mainServerIP = (String) teamObject.get("MainServerIP"); Object serversObject = teamObject.get("Servers"); - if(!(serversObject instanceof JSONArray)) return null; - JSONArray serversArray = (JSONArray) serversObject; + if (!(serversObject instanceof JSONArray serversArray)) return null; for(Object object : serversArray.toArray()) { - if(!(object instanceof JSONObject)) return null; - JSONObject serverObject = (JSONObject) object; + if (!(object instanceof JSONObject serverObject)) return null; String serverIP = (String) serverObject.get("IP"); if(serverIP.equals(mainServerIP)) return (String) serverObject.get("Name"); @@ -243,8 +234,8 @@ private int getArea(JSONObject regionObject) { if(regionObject == null) return 0; if(regionObject.get("area") == null) return 0; - if (regionObject.get("area") instanceof Long) - return ((Long) regionObject.get("area")).intValue(); + if (regionObject.get("area") instanceof Long area) + return Math.toIntExact(area); return ((Double) regionObject.get("area")).intValue(); } @@ -261,10 +252,10 @@ public void onFailure(IOException e) { return future; } - public static CompletableFuture setupCurrentServerData() { + public static @NotNull CompletableFuture setupCurrentServerData() { CompletableFuture future = new CompletableFuture<>(); - API.getAsync("https://nwapi.buildtheearth.net/api/teams/" + BuildTeamTools.getInstance().getConfig().getString(ConfigPaths.API_KEY), new API.ApiResponseCallback() { + getOneAsync("https://nwapi.buildtheearth.net/api/teams/" + BuildTeamTools.getInstance().getConfig().getString(ConfigPaths.API_KEY), new API.ApiResponseCallback() { @Override public void onResponse(String response) { try { @@ -370,8 +361,37 @@ public void onFailure(IOException e) { }); } - public static Warp getWarpByKey(String key) { - //TODO IMPLEMENT - return null; + private static void getOneAsync(String url, API.ApiResponseCallback callback) { + var path = BuildTeamTools.getInstance().getDataPath().resolve("buildteam.json"); + if (BuildTeamTools.getInstance().isDebug() && path.toFile().exists()) { + // If the file exists, read from it instead of making an API call + try { + String content = new String(java.nio.file.Files.readAllBytes(path)); + callback.onResponse(content); + BuildTeamTools.getInstance().getComponentLogger().warn("[DEBUG] Read connected buildteam from local file buildteam.json. Remove the file if you want up to date data."); + return; + } catch (IOException e) { + ChatHelper.logError("Failed to read from local buildteam.json: %s", e.getMessage()); + } + } + + API.getAsync(url, callback); + } + + private static void getAsync(String url, API.ApiResponseCallback callback) { + var path = BuildTeamTools.getInstance().getDataPath().resolve("buildteams.json"); + if (BuildTeamTools.getInstance().isDebug() && path.toFile().exists()) { + // If the file exists, read from it instead of making an API call + try { + String content = new String(java.nio.file.Files.readAllBytes(path)); + callback.onResponse(content); + BuildTeamTools.getInstance().getComponentLogger().warn("[DEBUG] Read buildteams from local file buildteams.json. Remove the file if you want up to date data."); + return; + } catch (IOException e) { + ChatHelper.logError("Failed to read from local buildteams.json: %s", e.getMessage()); + } + } + + API.getAsync(url, callback); } } diff --git a/src/main/java/net/buildtheearth/modules/network/api/OpenStreetMapAPI.java b/src/main/java/net/buildtheearth/modules/network/api/OpenStreetMapAPI.java index a43fdf9e..41cd903e 100644 --- a/src/main/java/net/buildtheearth/modules/network/api/OpenStreetMapAPI.java +++ b/src/main/java/net/buildtheearth/modules/network/api/OpenStreetMapAPI.java @@ -1,5 +1,7 @@ package net.buildtheearth.modules.network.api; +import net.buildtheearth.utils.ChatHelper; +import org.jetbrains.annotations.NotNull; import org.json.simple.JSONArray; import org.json.simple.JSONObject; @@ -12,23 +14,25 @@ public class OpenStreetMapAPI extends API { * @param coordinates The latitude & longitude coordinates to get the country, region & city/town from * @return The country name and country code belonging to this location */ - public static CompletableFuture getCountryFromLocationAsync(double[] coordinates) { + public static @NotNull CompletableFuture getCountryFromLocationAsync(double @NotNull [] coordinates) { CompletableFuture future = new CompletableFuture<>(); - String url = "https://nominatim.openstreetmap.org/reverse?lat=" + coordinates[0] + "&lon=" + coordinates[1] + "&zoom=10&format=geocodejson&accept-language=en"; + String url = "https://photon.komoot.io/reverse?lat=" + coordinates[0] + "&lon=" + coordinates[1] + "&lang=en"; + + ChatHelper.logDebug("Requesting country from location: %s", url); API.getAsync(url, new API.ApiResponseCallback() { @Override public void onResponse(String response) { JSONObject jsonObject = API.createJSONObject(response); - JSONArray featuresArray = (JSONArray) jsonObject.get("features"); - JSONObject featuresObject = (JSONObject) featuresArray.get(0); + ChatHelper.logDebug("Response from OpenStreetMap: %s", jsonObject); + + JSONObject featuresObject = (JSONObject) ((JSONArray) jsonObject.get("features")).getFirst(); JSONObject propertiesObject = (JSONObject) featuresObject.get("properties"); - JSONObject geoCodingObject = (JSONObject) propertiesObject.get("geocoding"); - String countryCodeCca2 = (String) geoCodingObject.get("country_code"); - String countryName = (String) geoCodingObject.get("country"); + String countryCodeCca2 = (String) propertiesObject.get("countrycode"); + String countryName = (String) propertiesObject.get("country"); future.complete(new String[]{countryName, countryCodeCca2}); } diff --git a/src/main/java/net/buildtheearth/modules/network/model/BuildTeam.java b/src/main/java/net/buildtheearth/modules/network/model/BuildTeam.java index d45ea275..3472a30c 100644 --- a/src/main/java/net/buildtheearth/modules/network/model/BuildTeam.java +++ b/src/main/java/net/buildtheearth/modules/network/model/BuildTeam.java @@ -1,12 +1,12 @@ package net.buildtheearth.modules.network.model; import lombok.Getter; +import net.buildtheearth.modules.navigation.components.warps.model.Warp; +import net.buildtheearth.modules.navigation.components.warps.model.WarpGroup; import net.buildtheearth.modules.network.NetworkModule; import net.buildtheearth.modules.network.api.API; import net.buildtheearth.modules.network.api.NetworkAPI; import net.buildtheearth.utils.ChatHelper; -import net.buildtheearth.modules.navigation.components.warps.model.Warp; -import net.buildtheearth.modules.navigation.components.warps.model.WarpGroup; import org.bukkit.entity.Player; import java.io.IOException; @@ -30,24 +30,28 @@ public class BuildTeam { @Getter private final boolean hasBTToolsInstalled; @Getter - private final Continent continent; - @Getter private final List regions; @Getter private final List warpGroups; + @Getter + private final boolean allowsTransfers; + @Getter + private final String tag; - public BuildTeam(String ID, String serverIP, String name, String blankName, String serverName, Continent continent, boolean isConnected, boolean hasBTToolsInstalled) { + public BuildTeam(String ID, String serverIP, String name, String blankName, String serverName, + boolean isConnected, boolean hasBTToolsInstalled, boolean allowsTransfers, String tag) { this.ID = ID; this.name = name; this.blankName = blankName; this.serverName = serverName; - this.continent = continent; this.isConnected = isConnected; this.hasBTToolsInstalled = hasBTToolsInstalled; this.regions = new ArrayList<>(); this.warpGroups = new ArrayList<>(); + this.allowsTransfers = allowsTransfers; + this.tag = tag; if(!isConnected) this.IP = serverIP; diff --git a/src/main/java/net/buildtheearth/utils/ChatHelper.java b/src/main/java/net/buildtheearth/utils/ChatHelper.java index 09b4df18..db250e57 100644 --- a/src/main/java/net/buildtheearth/utils/ChatHelper.java +++ b/src/main/java/net/buildtheearth/utils/ChatHelper.java @@ -34,6 +34,10 @@ public static void logError(String errorMessage, Object... objects) { Bukkit.getLogger().log(Level.INFO, ChatHelper.getErrorString(errorMessage, objects)); } + public static void logError(String errorMessage, Exception e, Object... objects) { + BuildTeamTools.getInstance().getComponentLogger().error(ChatHelper.getErrorComponent(errorMessage, objects), e); + } + public static void log(String string, Object... objects) { Bukkit.getConsoleSender().sendMessage( getConsoleString(string, objects)); } diff --git a/src/main/java/net/buildtheearth/utils/Item.java b/src/main/java/net/buildtheearth/utils/Item.java index 82cce476..69401a13 100644 --- a/src/main/java/net/buildtheearth/utils/Item.java +++ b/src/main/java/net/buildtheearth/utils/Item.java @@ -15,20 +15,28 @@ import org.bukkit.inventory.meta.ItemMeta; import org.bukkit.inventory.meta.LeatherArmorMeta; import org.bukkit.inventory.meta.SkullMeta; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import java.util.*; +import java.util.ArrayList; +import java.util.Base64; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; public class Item { - public static HashMap nonPlayerSkulls = new HashMap<>(); + public static final Map nonPlayerSkulls = new HashMap<>(); private ItemStack item; private Material material; private String displayName; private int amount = -1; - private ArrayList lore; + private List lore; private boolean hideAttributes; private boolean hideEnchantments; private final List canDestroyItems = new ArrayList<>(); @@ -61,7 +69,7 @@ public Item setAmount(int amount) { return this; } - public Item setLore(ArrayList lore) { + public Item setLore(List lore) { this.lore = lore; return this; } @@ -105,9 +113,9 @@ public ItemStack build() { else item.setAmount(1); - if(item.getEnchantments().keySet().size() == 0) - for (Enchantment en : this.enchantments.keySet()) - item.addUnsafeEnchantment(en, this.enchantments.get(en)); + if (item.getEnchantments().isEmpty()) + for (Map.Entry en : this.enchantments.entrySet()) + item.addUnsafeEnchantment(en.getKey(), en.getValue()); @@ -124,7 +132,7 @@ public ItemStack build() { if (this.hideEnchantments) itemmeta.addItemFlags(ItemFlag.HIDE_ENCHANTS); - if(!canDestroyItems.isEmpty()){ + /*if(!canDestroyItems.isEmpty()){ // TODO Marked for removal in 1.20.6, not fixed yet. //Set nameSpacedKeySet = new HashSet<>(); //for(String itemName : canDestroyItems) @@ -138,7 +146,7 @@ public ItemStack build() { //for(String itemName : canPlaceItems) // nameSpacedKeySet.add(NamespacedKey.minecraft(itemName)); //itemmeta.setPlaceableKeys(nameSpacedKeySet); - } + }*/ item.setItemMeta(itemmeta); return item; @@ -154,11 +162,7 @@ public static ItemStack create(Material material, int amount) { public static ItemStack create(Material material, String name) { ItemStack item = new ItemStack(material, 1); - ItemMeta itemmeta = item.getItemMeta(); - itemmeta.setDisplayName(name); - itemmeta.addItemFlags(ItemFlag.HIDE_ATTRIBUTES); - item.setItemMeta(itemmeta); - return item; + return edit(item, name); } public static ItemStack create(Material material, String name, int amount) { @@ -170,7 +174,7 @@ public static ItemStack create(Material material, String name, int amount) { return item; } - public static ItemStack create(Material material, String name, ArrayList lore) { + public static @NotNull ItemStack create(Material material, String name, List lore) { ItemStack item = new ItemStack(material, 1, (short)0); ItemMeta itemmeta = item.getItemMeta(); itemmeta.setDisplayName(name); @@ -180,7 +184,7 @@ public static ItemStack create(Material material, String name, ArrayList return item; } - public static ItemStack create(Material material, String name, short durability, ArrayList lore) { + public static @NotNull ItemStack create(Material material, String name, short durability, List lore) { ItemStack item = new ItemStack(material, 1, durability); ItemMeta itemmeta = item.getItemMeta(); itemmeta.setDisplayName(name); @@ -190,7 +194,7 @@ public static ItemStack create(Material material, String name, short durability, return item; } - public static ItemStack create(Material material, String name, int amount, ArrayList lore) { + public static @NotNull ItemStack create(Material material, String name, int amount, List lore) { ItemStack item = new ItemStack(material, amount); ItemMeta itemmeta = item.getItemMeta(); itemmeta.setDisplayName(name); @@ -200,7 +204,7 @@ public static ItemStack create(Material material, String name, int amount, Array return item; } - public static ItemStack createLeatherArmor(Material material, String name, Color color, ArrayList lore) { + public static @NotNull ItemStack createLeatherArmor(Material material, String name, Color color, List lore) { ItemStack item = new ItemStack(material); LeatherArmorMeta itemmeta = (LeatherArmorMeta)item.getItemMeta(); itemmeta.setDisplayName(name); @@ -211,7 +215,7 @@ public static ItemStack createLeatherArmor(Material material, String name, Color return item; } - public static ItemStack create(Material material, String name, ArrayList lore, Enchantment enchnt1, Integer level1) { + public static @NotNull ItemStack create(Material material, String name, List lore, Enchantment enchnt1, Integer level1) { ItemStack item = new ItemStack(material); item.addUnsafeEnchantment(enchnt1, level1); ItemMeta itemmeta = item.getItemMeta(); @@ -222,7 +226,7 @@ public static ItemStack create(Material material, String name, ArrayList return item; } - public static ItemStack create(Material material, String name, ArrayList lore, Enchantment enchnt1, Integer level1, Enchantment enchnt2, Integer level2) { + public static @NotNull ItemStack create(Material material, String name, List lore, Enchantment enchnt1, Integer level1, Enchantment enchnt2, Integer level2) { ItemStack item = new ItemStack(material); item.addUnsafeEnchantment(enchnt1, level1); item.addUnsafeEnchantment(enchnt2, level2); @@ -234,7 +238,7 @@ public static ItemStack create(Material material, String name, ArrayList return item; } - public static ItemStack create(Material material, String name, ArrayList lore, Enchantment enchnt1, Integer level1, Enchantment enchnt2, Integer level2, Enchantment enchnt3, Integer level3) { + public static @NotNull ItemStack create(Material material, String name, List lore, Enchantment enchnt1, Integer level1, Enchantment enchnt2, Integer level2, Enchantment enchnt3, Integer level3) { ItemStack item = new ItemStack(material); item.addUnsafeEnchantment(enchnt1, level1); item.addUnsafeEnchantment(enchnt2, level2); @@ -247,7 +251,7 @@ public static ItemStack create(Material material, String name, ArrayList return item; } - public static ItemStack createLeatherArmor(Material material, Color color) { + public static @NotNull ItemStack createLeatherArmor(Material material, Color color) { ItemStack item = new ItemStack(material); LeatherArmorMeta itemmeta = (LeatherArmorMeta)item.getItemMeta(); itemmeta.setColor(color); @@ -256,7 +260,7 @@ public static ItemStack createLeatherArmor(Material material, Color color) { return item; } - public static ItemStack createLeatherArmor(Material material, String name, Color color, ArrayList lore, Enchantment enchnt1, Integer level1) { + public static @NotNull ItemStack createLeatherArmor(Material material, String name, Color color, List lore, Enchantment enchnt1, Integer level1) { ItemStack item = new ItemStack(material); item.addUnsafeEnchantment(enchnt1, level1); LeatherArmorMeta itemmeta = (LeatherArmorMeta)item.getItemMeta(); @@ -268,7 +272,7 @@ public static ItemStack createLeatherArmor(Material material, String name, Color return item; } - public static ItemStack createLeatherArmor(Material material, String name, Color color, ArrayList lore, Enchantment enchnt1, Integer level1, Enchantment enchnt2, Integer level2) { + public static @NotNull ItemStack createLeatherArmor(Material material, String name, Color color, List lore, Enchantment enchnt1, Integer level1, Enchantment enchnt2, Integer level2) { ItemStack item = new ItemStack(material); item.addUnsafeEnchantment(enchnt1, level1); item.addUnsafeEnchantment(enchnt2, level2); @@ -281,7 +285,7 @@ public static ItemStack createLeatherArmor(Material material, String name, Color return item; } - public static ItemStack createLeatherArmor(Material material, String name, Color color, ArrayList lore, Enchantment enchnt1, Integer level1, Enchantment enchnt2, Integer level2, Enchantment enchnt3, Integer level3) { + public static @NotNull ItemStack createLeatherArmor(Material material, String name, Color color, List lore, Enchantment enchnt1, Integer level1, Enchantment enchnt2, Integer level2, Enchantment enchnt3, Integer level3) { ItemStack item = new ItemStack(material); item.addUnsafeEnchantment(enchnt1, level1); item.addUnsafeEnchantment(enchnt2, level2); @@ -295,21 +299,7 @@ public static ItemStack createLeatherArmor(Material material, String name, Color return item; } - public static ItemStack createPlayerHead(String name, String owner) { - ItemStack item = XMaterial.PLAYER_HEAD.parseItem(); - - if(item == null) - return null; - - SkullMeta itemmeta = (SkullMeta)item.getItemMeta(); - itemmeta.setDisplayName(name); - itemmeta.setOwner(owner); - itemmeta.addItemFlags(ItemFlag.HIDE_ATTRIBUTES); - item.setItemMeta(itemmeta); - return item; - } - - public static ItemStack createPlayerHead(String name, String owner, ArrayList lore) { + public static @Nullable ItemStack createPlayerHead(String name, String owner) { ItemStack item = XMaterial.PLAYER_HEAD.parseItem(); if(item == null) @@ -318,13 +308,12 @@ public static ItemStack createPlayerHead(String name, String owner, ArrayList lore) { + public static @Nullable ItemStack createPlayerHead(String name, String owner, List lore) { ItemStack item = XMaterial.PLAYER_HEAD.parseItem(); if(item == null) @@ -339,17 +328,20 @@ public static ItemStack createPlayerHead(String name, String owner, int amount, return item; } - public static ItemStack edit(ItemStack item, Material material) { + @Contract("_, _ -> param1") + public static @NotNull ItemStack edit(@NotNull ItemStack item, Material material) { item.setType(material); return item; } - public static ItemStack edit(ItemStack item, int amount) { + @Contract("_, _ -> param1") + public static @NotNull ItemStack edit(@NotNull ItemStack item, int amount) { item.setAmount(amount); return item; } - public static ItemStack edit(ItemStack item, String name) { + @Contract("_, _ -> param1") + public static @NotNull ItemStack edit(@NotNull ItemStack item, String name) { ItemMeta itemmeta = item.getItemMeta(); itemmeta.setDisplayName(name); itemmeta.addItemFlags(ItemFlag.HIDE_ATTRIBUTES); @@ -357,7 +349,8 @@ public static ItemStack edit(ItemStack item, String name) { return item; } - public static ItemStack edit(ItemStack item, int amount, String name) { + @Contract("_, _, _ -> param1") + public static @NotNull ItemStack edit(@NotNull ItemStack item, int amount, String name) { item.setAmount(amount); ItemMeta itemmeta = item.getItemMeta(); itemmeta.setDisplayName(name); @@ -366,7 +359,8 @@ public static ItemStack edit(ItemStack item, int amount, String name) { return item; } - public static ItemStack edit(ItemStack item, ArrayList lore) { + @Contract("_, _ -> param1") + public static @NotNull ItemStack edit(@NotNull ItemStack item, List lore) { ItemMeta itemmeta = item.getItemMeta(); itemmeta.setLore(lore); itemmeta.addItemFlags(ItemFlag.HIDE_ATTRIBUTES); @@ -374,7 +368,8 @@ public static ItemStack edit(ItemStack item, ArrayList lore) { return item; } - public static ItemStack edit(ItemStack item, int amount, String name, ArrayList lore) { + @Contract("_, _, _, _ -> param1") + public static @NotNull ItemStack edit(@NotNull ItemStack item, int amount, String name, List lore) { item.setAmount(amount); ItemMeta itemmeta = item.getItemMeta(); itemmeta.setDisplayName(name); @@ -384,7 +379,7 @@ public static ItemStack edit(ItemStack item, int amount, String name, ArrayList< return item; } - public static ItemStack fromUniqueMaterialString(String materialString) { + public static @Nullable ItemStack fromUniqueMaterialString(String materialString) { Material material = Material.matchMaterial(materialString); if(material != null) return XMaterial.matchXMaterial(material).parseItem(); @@ -396,7 +391,7 @@ public static ItemStack fromUniqueMaterialString(String materialString) { return null; } - public static String getUniqueMaterialString(ItemStack item) { + public static @NotNull String getUniqueMaterialString(ItemStack item) { if(CommonModule.getInstance().getVersionComponent().is_1_12()) return XMaterial.matchXMaterial(item).getId() + ":" + XMaterial.matchXMaterial(item).getData(); else @@ -404,7 +399,7 @@ public static String getUniqueMaterialString(ItemStack item) { } - public static String getUniqueMaterialString(XMaterial material) { + public static @NotNull String getUniqueMaterialString(@NotNull XMaterial material) { return getUniqueMaterialString(material.parseItem()); } @@ -420,7 +415,7 @@ public static String getUniqueMaterialString(XMaterial[] materials) { return s.toString(); } - public static XMaterial convertStringToXMaterial(String materialString) { + public static @Nullable XMaterial convertStringToXMaterial(String materialString) { XMaterial material; if(XMaterial.matchXMaterial(materialString).isPresent()) @@ -448,8 +443,9 @@ public static BlockType convertXMaterialToBlockType(XMaterial material) { return bt; } - public static String createStringFromItemList(ArrayList items) throws IllegalArgumentException { - StringBuilder s = new StringBuilder(items.get(0)); + + public static @NotNull String createStringFromItemList(@NotNull List items) throws IllegalArgumentException { + StringBuilder s = new StringBuilder(items.getFirst()); for (int i = 1; i < items.size(); i++) if(XMaterial.matchXMaterial(items.get(i)).isPresent()) { @@ -461,14 +457,14 @@ public static String createStringFromItemList(ArrayList items) throws Il return s.toString(); } - public static ItemStack createCustomHeadTextureURL(String url, String name, ArrayList lore) { + public static ItemStack createCustomHeadTextureURL(String url, String name, List lore) { byte[] encodedByteData = Base64.getEncoder().encode(String.format("{textures:{SKIN:{url:\"%s\"}}}", url).getBytes()); String encodedData = new String(encodedByteData); return createCustomHeadBase64(encodedData, name, lore); } - public static ItemStack createCustomHeadBase64(String base64, String name, ArrayList lore) { + public static @Nullable ItemStack createCustomHeadBase64(String base64, String name, List lore) { if (nonPlayerSkulls.containsKey(base64 + name + lore)) return nonPlayerSkulls.get(base64 + name + lore); @@ -519,8 +515,7 @@ private static void mutateItemMeta(SkullMeta meta, String b64) { } - - private static GameProfile makeProfile(String b64) { + private static @NotNull GameProfile makeProfile(@NotNull String b64) { UUID id = new UUID( b64.substring(b64.length() - 20).hashCode(), b64.substring(b64.length() - 10).hashCode() diff --git a/src/main/java/net/buildtheearth/utils/Utils.java b/src/main/java/net/buildtheearth/utils/Utils.java index 150dbfa9..d1446a9d 100644 --- a/src/main/java/net/buildtheearth/utils/Utils.java +++ b/src/main/java/net/buildtheearth/utils/Utils.java @@ -1,16 +1,5 @@ package net.buildtheearth.utils; -import com.google.common.io.ByteArrayDataOutput; -import com.google.common.io.ByteStreams; -import net.buildtheearth.BuildTeamTools; -import net.buildtheearth.modules.network.NetworkModule; -import org.bukkit.Bukkit; -import org.bukkit.ChatColor; -import org.bukkit.Material; -import org.bukkit.World; -import org.bukkit.entity.Player; -import org.bukkit.inventory.ItemStack; - import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; @@ -44,17 +33,6 @@ public static String[] splitStringByLineLength(String input, int maxLineLength, return lines.toArray(new String[0]); } - public static void sendPlayerToServer(Player player, String server) { - if(NetworkModule.getInstance().getBuildTeam() == null - || !NetworkModule.getInstance().getBuildTeam().isConnected()) - return; - - ByteArrayDataOutput out = ByteStreams.newDataOutput(); - out.writeUTF("Connect"); - out.writeUTF(server); - player.sendPluginMessage(BuildTeamTools.getInstance(), "BungeeCord", out.toByteArray()); - } - public static Object pickRandom(Object[] array) { if (array.length == 0) return null; @@ -66,11 +44,9 @@ public static Object pickRandom(Object[] array) { /** * Converts the given Time to a time string * - * @param p player for translation * @param time time in Milliseconds - * @return */ - public static String toDate(Player p, long time) { + public static String toDate(long time) { String s = ""; int days = 0; int hours = 0; @@ -79,22 +55,21 @@ public static String toDate(Player p, long time) { if (time > 86400000) { //Tage days = (int) (time / 86400000); - time = time - (86400000 * days); + time = time - (86400000L * days); } if (time > 3600000) { //Stunden hours = (int) (time / 3600000); - time = time - (3600000 * hours); + time = time - (3600000L * hours); } if (time > 60000) { //Minuten minutes = (int) (time / 60000); - time = time - (60000 * minutes); + time = time - (60000L * minutes); } if (time > 1000) { //Sekunden seconds = (int) (time / 1000); - time = time - (1000 * seconds); } if (days > 0) { @@ -103,13 +78,13 @@ public static String toDate(Player p, long time) { else s = s + days + " Days, "; } - if (hours > 0 | days > 0) { + if (hours > 0 || days > 0) { if (hours == 1) s = s + hours + " Hour"; else s = s + hours + " Hours"; } - if ((minutes > 0 | hours > 0) & days == 0) { + if ((minutes > 0 || hours > 0) && days == 0) { if (hours > 0) s = s + ", "; @@ -118,7 +93,7 @@ public static String toDate(Player p, long time) { else s = s + minutes + " Minutes"; } - if ((seconds > 0 | minutes > 0) & hours == 0 & days == 0) { + if ((seconds > 0 || minutes > 0) && hours == 0 && days == 0) { if (minutes > 0) s = s + ", "; diff --git a/src/main/java/net/buildtheearth/utils/io/ConfigPaths.java b/src/main/java/net/buildtheearth/utils/io/ConfigPaths.java index c8b7a153..f6421547 100644 --- a/src/main/java/net/buildtheearth/utils/io/ConfigPaths.java +++ b/src/main/java/net/buildtheearth/utils/io/ConfigPaths.java @@ -25,13 +25,23 @@ public static class Navigation { // Navigator.MainMenuItems private static final String NAVIGATOR_MAIN_MENU = "main-menu-items."; private static final String BUILD_ITEM = NAVIGATOR_MAIN_MENU + "build-item."; - public static final String BUILD_ITEM_ENABLED = BUILD_ITEM + "build-enabled"; - public static final String BUILD_ITEM_ACTION = BUILD_ITEM + "build-action"; + public static final String BUILD_ITEM_ENABLED = BUILD_ITEM + "enabled"; + public static final String BUILD_ITEM_ACTION = BUILD_ITEM + "action"; private static final String TUTORIALS_ITEM = NAVIGATOR_MAIN_MENU + "tutorial-item."; - public static final String TUTORIALS_ITEM_ENABLED = TUTORIALS_ITEM + "tutorial-enabled"; - public static final String TUTORIALS_ITEM_ACTION = TUTORIALS_ITEM + "tutorial-action"; + public static final String TUTORIALS_ITEM_ENABLED = TUTORIALS_ITEM + "enabled"; + public static final String TUTORIALS_ITEM_ACTION = TUTORIALS_ITEM + "action"; + private static final String PLOTSYSTEM_ITEM = NAVIGATOR_MAIN_MENU + "plotsystem-item."; + public static final String PLOTSYSTEM_ITEM_ENABLED = PLOTSYSTEM_ITEM + "enabled"; + public static final String PLOTSYSTEM_ITEM_ACTION = PLOTSYSTEM_ITEM + "action"; + + private static final String EXPLORE_ITEM = NAVIGATOR_MAIN_MENU + "explore-item."; + public static final String EXPLORE_ITEM_ENABLED = EXPLORE_ITEM + "enabled"; + + // Navigator.Warps + private static final String NAVIGATOR_WARPS = "warps."; + public static final String WARPS_GROUP_SORTING_MODE = NAVIGATOR_WARPS + "sorting-mode"; } public static class PlotSystem { diff --git a/src/main/resources/modules/navigation/config.yml b/src/main/resources/modules/navigation/config.yml index 9ee3f9ee..0eed449b 100644 --- a/src/main/resources/modules/navigation/config.yml +++ b/src/main/resources/modules/navigation/config.yml @@ -8,7 +8,6 @@ # | [Contacts - Discord] BuildTheEarth Staff Discord, @minefact # ---------------------------------------------------------------------------------------------- - # Configures the navigator item in the players hotbar navigator-hotbar-item: # Enables or disables the navigator hotbar item [true|false] @@ -16,22 +15,37 @@ navigator-hotbar-item: # The hotbar slot into which the navigator item should be placed nav-slot: 0 +# ACTION - Below you can define actions for some items. There is a comment when a default action exists, else it will error out. +# Command & Message - just specify it like you would do it in the chat. E.g.: /tp 100 50 100 +# Transfer - Transfer the player to the specified team. E.g.: transfer: +# Switch - Switch the player to the specified server (on the same proxy). E.g.: switch:> + # Specifies which menu items should be enabled main-menu-items: # Configures the build item inside the navigator build-item: # Enables or disables the build item inside the navigator [true|false] - build-enabled: true - # (Optional) Execute a custom command or send a message when the build item is clicked [/command|message] - build-action: /command + enabled: false + action: /command + plotsystem-item: + # Enables or disables the Plot System item inside the navigator [true|false] + enabled: true + action: /command + explore-item: + # Enables or disables the explore item inside the navigator [true|false] + # Left click opens BTT Warp System, right click Buildteam Explore Menu + enabled: true # Configures the tutorial item inside the navigator tutorial-item: # Enables or disables the tutorial item inside the navigator [true|false] - tutorial-enabled: false - # (Optional) Execute a custom command or send a message when the tutorial item is clicked [/command|message] - tutorial-action: /command - + enabled: false + # (Optional) If nothing is set/changed here, it will open up the btt tutorial menu + action: /command +warps: + # The order in which the warps should be displayed in the navigator + # Possible values: default (same as the creation order), name + sorting-mode: default # NOTE: Do not change config-version: 1.4 \ No newline at end of file diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index d4e47846..dece42e8 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -2,13 +2,14 @@ name: BuildTeamTools main: net.buildtheearth.BuildTeamTools api-version: 1.13 version: 0.1.3-alpha -author: MineFact, frikandelworst, George112n, ELgamer, SirJodelstein -softdepend: [WorldEdit, HeadDatabase] +author: MineFact, frikandelworst, George112n, ELgamer, SirJodelstein, Zoriot +softdepend: [WorldEdit, HeadDatabase, PlotSystem-Terra] + commands: - buildteam: - description: Main command of the Build Team plugin. - usage: /buildteam - aliases: [btt, buildteamtools] + buildteamtools: + description: Main command of the Build Team Tools plugin. + usage: /buildteamtools + aliases: [btt] generate: description: Allows usage of the Generator Tool. usage: /generate @@ -21,9 +22,10 @@ commands: description: Command for managing warps. usage: /warp aliases: [warps] - temporary: - description: Temporary test command. - usage: /temporary + warpsbt: + description: Opens the warp menu for the specified BuildTeam + usage: /warpsbt + aliases: [ wbt ] kml: description: kml pasting and parsing usage: /kml @@ -44,6 +46,10 @@ commands: usage: /blockpalette [filter|menu|filter ...] aliases: [bp, blocks] permission: blockpalette.use + buildteam: + description: Sends the player to the specified BuildTeam + usage: /buildteam + aliases: [bt] permissions: blockpalette.use: description: Allows players to use /blockpalette