diff --git a/build.gradle.kts b/build.gradle.kts index aca58ce..d7b2034 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -62,6 +62,10 @@ repositories { maven { url = uri("https://jitpack.io") } maven("https://repo.bluecolored.de/releases") + + maven { + url = uri("https://repo.essentialsx.net/releases/") + } } dependencies { @@ -91,6 +95,9 @@ dependencies { compileOnly("com.fastasyncworldedit:FastAsyncWorldEdit-Bukkit") { isTransitive = false } compileOnly(libs.paper.api) compileOnly(libs.bluemap.api) + compileOnly("net.essentialsx:EssentialsX:2.19.0") { + isTransitive = false + } } val versionDetails: groovy.lang.Closure by extra diff --git a/src/main/java/net/buildtheearth/buildteamtools/modules/navigation/components/bluemap/BluemapComponent.java b/src/main/java/net/buildtheearth/buildteamtools/modules/navigation/components/bluemap/BluemapComponent.java index 4eb97a5..719abeb 100644 --- a/src/main/java/net/buildtheearth/buildteamtools/modules/navigation/components/bluemap/BluemapComponent.java +++ b/src/main/java/net/buildtheearth/buildteamtools/modules/navigation/components/bluemap/BluemapComponent.java @@ -15,7 +15,6 @@ import net.buildtheearth.buildteamtools.modules.network.model.BuildTeam; import net.buildtheearth.buildteamtools.utils.io.ConfigPaths; import net.buildtheearth.buildteamtools.utils.io.ConfigUtil; -import net.buildtheearth.model.GeographicalCoordinate; import net.buildtheearth.model.MinecraftCoordinate; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; @@ -151,7 +150,7 @@ private void registerWarpsForWorld(BlueMapAPI api, @NotNull WarpGroup warpGroup, private void addWarpMarker(@NotNull MarkerSet markerSet, @NotNull Warp warp) { // Convert geographic coordinates to Minecraft world coordinates try { - MinecraftCoordinate coordinate = Projection.toMinecraft(new GeographicalCoordinate(warp.getLat(), warp.getLon())); + MinecraftCoordinate coordinate = Projection.toMinecraft(warp.getCoordinate()); // Create a POI marker for the warp POIMarker marker = POIMarker.builder() diff --git a/src/main/java/net/buildtheearth/buildteamtools/modules/navigation/components/tpll/TpllComponent.java b/src/main/java/net/buildtheearth/buildteamtools/modules/navigation/components/tpll/TpllComponent.java index 49e59fe..e41acc4 100644 --- a/src/main/java/net/buildtheearth/buildteamtools/modules/navigation/components/tpll/TpllComponent.java +++ b/src/main/java/net/buildtheearth/buildteamtools/modules/navigation/components/tpll/TpllComponent.java @@ -89,28 +89,28 @@ public void processQueueForPlayer(@NonNull Player player) { * Sends a plugin message with a tpll request to the target server and sends the player there. * * @param player The player to send to the new server. - * @param coordinates The coordinates to send the player to on join. + * @param coordinate The coordinate to send the player to on join. * @param targetServerName The server to send the player to. */ - public void tpllPlayer(@NotNull Player player, double @NotNull [] coordinates, String targetServerName) { + public void tpllPlayer(@NotNull Player player, @NotNull GeographicalCoordinate coordinate, 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(); out.writeUTF("TPLL"); out.writeUTF(targetServerName); out.writeUTF(player.getUniqueId().toString()); - out.writeUTF(String.valueOf(coordinates[0])); - out.writeUTF(String.valueOf(coordinates[1])); + out.writeUTF(String.valueOf(coordinate.latitude())); + out.writeUTF(String.valueOf(coordinate.longitude())); player.sendPluginMessage(BuildTeamTools.getInstance(), "btt:buildteam", out.toByteArray()); // Switch the player to the target server ChatHelper.logDebug("Teleported player to the target server."); } - public void tpllPlayerTransfer(@NotNull Player player, double @NotNull [] coordinates, String ip) { + public void tpllPlayerTransfer(@NotNull Player player, @NotNull GeographicalCoordinate coordinate, String ip) { ByteArrayDataOutput out = ByteStreams.newDataOutput(); - out.writeDouble(coordinates[0]); - out.writeDouble(coordinates[1]); + out.writeDouble(coordinate.latitude()); + out.writeDouble(coordinate.longitude()); player.storeCookie(TPLL_COOKIE_KEY, out.toByteArray()); NavUtils.transferPlayer(player, ip); } diff --git a/src/main/java/net/buildtheearth/buildteamtools/modules/navigation/components/tpll/listeners/TpllListener.java b/src/main/java/net/buildtheearth/buildteamtools/modules/navigation/components/tpll/listeners/TpllListener.java index 6cbaa9e..8df177e 100644 --- a/src/main/java/net/buildtheearth/buildteamtools/modules/navigation/components/tpll/listeners/TpllListener.java +++ b/src/main/java/net/buildtheearth/buildteamtools/modules/navigation/components/tpll/listeners/TpllListener.java @@ -8,6 +8,7 @@ import net.buildtheearth.buildteamtools.modules.network.api.OpenStreetMapAPI; import net.buildtheearth.buildteamtools.modules.network.model.BuildTeam; import net.buildtheearth.buildteamtools.modules.network.model.Region; +import net.buildtheearth.model.GeographicalCoordinate; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerCommandPreprocessEvent; @@ -27,9 +28,7 @@ public class TpllListener implements Listener { // Proxy manager to handle network-related operations private final NetworkModule networkModule = NetworkModule.getInstance(); - // Latitude and longitude coordinates for teleportation - private double lon; - private double lat; + private GeographicalCoordinate coordinate; // Target server name for teleportation private BuildTeam targetBuildTeam; @@ -42,7 +41,7 @@ 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); + ChatHelper.logDebug("Intercepted TPLL command wit lat and lon: %s %s", coordinate.latitude(), coordinate.longitude()); // Check if teleportation interception is required shouldIntercept().thenAcceptAsync(shouldIntercept -> { @@ -51,11 +50,11 @@ public void onTpll(PlayerCommandPreprocessEvent event) { 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()); + NavigationModule.getInstance().getTpllComponent().tpllPlayer(event.getPlayer(), coordinate, + targetBuildTeam.getServerName()); } else if (type == NavUtils.NavSwitchType.TRANSFER) { - NavigationModule.getInstance().getTpllComponent().tpllPlayerTransfer(event.getPlayer(), - new double[]{lat, lon}, targetBuildTeam.getIP()); + NavigationModule.getInstance().getTpllComponent().tpllPlayerTransfer(event.getPlayer(), coordinate, + targetBuildTeam.getIP()); } event.setCancelled(true); } @@ -89,8 +88,7 @@ private boolean isTpllCommand(@NotNull PlayerCommandPreprocessEvent event) { return false; } - this.lat = oLat; - this.lon = oLon; + coordinate = new GeographicalCoordinate(oLat, oLon); return true; } @@ -100,7 +98,7 @@ private boolean isTpllCommand(@NotNull PlayerCommandPreprocessEvent event) { * @return A CompletableFuture representing whether teleportation interception is required. */ private @NotNull CompletableFuture shouldIntercept() { - return OpenStreetMapAPI.getCountryFromLocationAsync(new double[]{lat, lon}) + return OpenStreetMapAPI.getCountryFromLocationAsync(coordinate) .thenComposeAsync(address -> { if (address == null) return CompletableFuture.completedFuture(false); diff --git a/src/main/java/net/buildtheearth/buildteamtools/modules/navigation/components/warps/WarpMigrator.java b/src/main/java/net/buildtheearth/buildteamtools/modules/navigation/components/warps/WarpMigrator.java new file mode 100644 index 0000000..47402c1 --- /dev/null +++ b/src/main/java/net/buildtheearth/buildteamtools/modules/navigation/components/warps/WarpMigrator.java @@ -0,0 +1,27 @@ +package net.buildtheearth.buildteamtools.modules.navigation.components.warps; + +import net.buildtheearth.buildteamtools.modules.navigation.components.warps.migrators.EssentialsWarpMigrator; +import net.buildtheearth.buildteamtools.modules.navigation.components.warps.migrators.IWarpMigrator; +import net.buildtheearth.buildteamtools.modules.navigation.components.warps.model.MigrationResult; +import net.buildtheearth.buildteamtools.modules.navigation.components.warps.model.WarpMigrationSource; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.Contract; +import org.jspecify.annotations.NonNull; + +import java.util.concurrent.CompletableFuture; + +public class WarpMigrator { + + private final IWarpMigrator migrator; + + @Contract(pure = true) + public WarpMigrator(@NonNull WarpMigrationSource source) { + this.migrator = switch (source) { + case ESSENTIALS -> new EssentialsWarpMigrator(); + }; + } + + public CompletableFuture migrate(Player player) { + return migrator.migrate(player); + } +} diff --git a/src/main/java/net/buildtheearth/buildteamtools/modules/navigation/components/warps/WarpsComponent.java b/src/main/java/net/buildtheearth/buildteamtools/modules/navigation/components/warps/WarpsComponent.java index 5b2bb73..d76a496 100644 --- a/src/main/java/net/buildtheearth/buildteamtools/modules/navigation/components/warps/WarpsComponent.java +++ b/src/main/java/net/buildtheearth/buildteamtools/modules/navigation/components/warps/WarpsComponent.java @@ -32,6 +32,7 @@ import java.util.HashMap; import java.util.List; +import java.util.Objects; import java.util.UUID; import java.util.concurrent.CompletableFuture; @@ -64,7 +65,8 @@ public void addWarpToQueue(@NotNull ByteArrayDataInput in, Player player) { return; } - Location targetWarpLocation = NavUtils.getLocationFromCoordinatesYawPitch(new GeographicalCoordinate(warp.getLat(), warp.getLon()), warp.getYaw(), warp.getPitch()); + Location targetWarpLocation = NavUtils.getLocationFromCoordinatesYawPitch(warp.getCoordinate(), warp.getYaw(), + warp.getPitch()); targetWarpLocation.setY(warp.getY()); targetWarpLocation.setWorld(Bukkit.getWorld(warp.getWorldName())); @@ -106,7 +108,7 @@ public void warpPlayer(Player player, @NotNull Warp warp) { BuildTeam currentTeam = NetworkModule.getInstance().getBuildTeam(); if (currentTeam != null && warp.getWarpGroup().getBuildTeam().getID().equals(currentTeam.getID())) { ChatHelper.logDebug("Warping player %s to warp %s", player.getName(), warp.getName()); - Location loc = NavUtils.getLocationFromCoordinatesYawPitch(new GeographicalCoordinate(warp.getLat(), warp.getLon()), warp.getYaw(), warp.getPitch()); + Location loc = NavUtils.getLocationFromCoordinatesYawPitch(warp.getCoordinate(), warp.getYaw(), warp.getPitch()); if (loc.getWorld() == null) { World world = Bukkit.getWorld(warp.getWorldName()) == null ? player.getWorld() : Bukkit.getWorld(warp.getWorldName()); @@ -169,7 +171,7 @@ public static void createWarp(@NonNull Player creator, WarpGroup group) { GeographicalCoordinate coordinate = Projection.toGeo(new MinecraftCoordinate(location.getX(), location.getZ())); //Get the country belonging to the coordinates - CompletableFuture future = OpenStreetMapAPI.getCountryFromLocationAsync(new double[]{coordinate.latitude(), coordinate.longitude()}); + CompletableFuture future = OpenStreetMapAPI.getCountryFromLocationAsync(coordinate); future.thenAccept(result -> { String regionName = result[0]; @@ -187,7 +189,8 @@ public static void createWarp(@NonNull Player creator, WarpGroup group) { 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(), coordinate.latitude(), coordinate.longitude(), location.getY(), location.getYaw(), location.getPitch(), false); + Warp warp = new Warp(group, name, countryCodeCCA2, "cca2", null, null, null, location.getWorld().getName(), + coordinate, location.getY(), location.getYaw(), location.getPitch(), false); Bukkit.getScheduler().runTask(BuildTeamTools.getInstance(), () -> new WarpEditMenu(creator, warp, false, true)); @@ -203,6 +206,43 @@ public static void createWarp(@NonNull Player creator, WarpGroup group) { } } + /** + * Creates a warp at the given location. + */ + public static void createWarp(@NonNull Location location, String name, WarpGroup group, Player creator) throws OutOfProjectionBoundsException { + GeographicalCoordinate coordinates = Projection.toGeo(new MinecraftCoordinate(location.getX(), location.getZ())); + + //Get the country belonging to the coordinates + CompletableFuture future = OpenStreetMapAPI.getCountryFromLocationAsync(coordinates); + + future.thenAccept(result -> { + String regionName = result[0]; + String countryCodeCCA2 = result[1].toUpperCase(); + + BuildTeamTools.getInstance().getComponentLogger().debug("Creating warp at {}", location); + + //Check if the team owns this region/country + boolean ownsRegion = NetworkModule.getInstance().ownsRegion(regionName, countryCodeCCA2); + + if (!ownsRegion) { + creator.sendMessage(ChatHelper.getErrorString("Warp %s cannot be created. This team does not own the country %s" + + " (Country code %s)!", name, regionName, countryCodeCCA2)); + return; + } + + // Create an instance of the warp POJO + Warp warp = new Warp(group, name, countryCodeCCA2, "cca2", null, null, null, location.getWorld().getName(), + coordinates, location.getY(), location.getYaw(), location.getPitch(), false); + + Objects.requireNonNull(NetworkModule.getInstance().getBuildTeam()).createWarp(creator, warp, true); + }).exceptionally(e -> { + BuildTeamTools.getInstance().getComponentLogger().error("An error occurred while creating the warp!", e); + Throwable cause = e.getCause() != null ? e.getCause() : e; + creator.sendMessage(ChatHelper.getErrorString("Failed to create warp %s: %s", name, cause.getMessage())); + return null; + }); + } + public void createWarpGroup(@NotNull Player creator) { // Create a default name for the warp diff --git a/src/main/java/net/buildtheearth/buildteamtools/modules/navigation/components/warps/commands/WarpCommand.java b/src/main/java/net/buildtheearth/buildteamtools/modules/navigation/components/warps/commands/WarpCommand.java index 55852c4..6cc6388 100644 --- a/src/main/java/net/buildtheearth/buildteamtools/modules/navigation/components/warps/commands/WarpCommand.java +++ b/src/main/java/net/buildtheearth/buildteamtools/modules/navigation/components/warps/commands/WarpCommand.java @@ -2,8 +2,11 @@ import com.alpsbte.alpslib.utils.ChatHelper; import net.buildtheearth.buildteamtools.modules.navigation.NavigationModule; +import net.buildtheearth.buildteamtools.modules.navigation.components.warps.WarpMigrator; import net.buildtheearth.buildteamtools.modules.navigation.components.warps.WarpsComponent; +import net.buildtheearth.buildteamtools.modules.navigation.components.warps.model.MigrationResult; import net.buildtheearth.buildteamtools.modules.navigation.components.warps.model.Warp; +import net.buildtheearth.buildteamtools.modules.navigation.components.warps.model.WarpMigrationSource; import net.buildtheearth.buildteamtools.modules.network.NetworkModule; import net.buildtheearth.buildteamtools.modules.network.model.BuildTeam; import net.buildtheearth.buildteamtools.modules.network.model.Permissions; @@ -15,6 +18,7 @@ import org.jetbrains.annotations.Nullable; import org.jspecify.annotations.NonNull; +import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -23,13 +27,13 @@ public class WarpCommand implements CommandExecutor, TabCompleter { @Override public boolean onCommand(@NonNull CommandSender sender, @NonNull Command command, @NonNull String label, String @NonNull [] args) { if (!(sender instanceof Player player)) { - sender.sendMessage(ChatHelper.getErrorString("This command can only be used by a player!")); + sender.sendMessage(ChatHelper.getErrorComponent("This command can only be used by a player!")); return true; } - // Check if the build team is loaded if (NetworkModule.getInstance().getBuildTeam() == null) { - sender.sendMessage(ChatHelper.getErrorString("The Warp Module is currently disabled because the Build Team failed to load!")); + sender.sendMessage(ChatHelper.getErrorComponent("The Warp Module is currently disabled because the Build Team " + + "failed to load!")); return true; } @@ -39,49 +43,107 @@ public boolean onCommand(@NonNull CommandSender sender, @NonNull Command command return true; } - // WARP CREATE if (args[0].equalsIgnoreCase("create")) { - // 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; - } + return handleCreateCommand(player, args); + } - // Check if the command has only one argument - if (args.length > 1) { - player.sendMessage(ChatHelper.getErrorString("Usage: /warp create")); - return true; - } + if (args[0].equalsIgnoreCase("migrate")) { + return handleMigrateCommand(player, args); + } + + return handleWarpTeleport(player, args); + } + + private boolean handleCreateCommand(@NonNull Player player, String @NonNull [] args) { + if (!player.hasPermission(Permissions.WARP_CREATE)) { + player.sendMessage(ChatHelper.getErrorComponent("You don't have the required %s to %s warps.", "permission", + "create")); + return true; + } + + if (args.length > 1) { + player.sendMessage(ChatHelper.getErrorComponent("Usage: /warp create")); + return true; + } + + player.sendActionBar(ChatHelper.getStandardComponent(false, "Creating the warp...")); + WarpsComponent.createWarp(player); + return true; + } - player.sendActionBar(ChatHelper.getStandardString(false, "Creating the warp...")); + private boolean handleMigrateCommand(@NonNull Player player, String @NonNull [] args) { + if (!player.hasPermission(Permissions.WARP_MIGRATE)) { + player.sendMessage(ChatHelper.getErrorString("You don't have the required %s to %s warps.", "permission", + "migrate")); + return true; + } + + if (args.length != 2) { + player.sendMessage(ChatHelper.getErrorComponent("Usage: /warp migrate ")); + player.sendMessage(ChatHelper.getErrorComponent("Valid sources are: %s", + Arrays.toString(WarpMigrationSource.values()))); + return true; + } - WarpsComponent.createWarp(player); + WarpMigrationSource source = WarpMigrationSource.fromString(args[1].toLowerCase()); + if (source == null) { + player.sendMessage(ChatHelper.getErrorComponent("Invalid source: %s", args[1])); + player.sendMessage(ChatHelper.getErrorComponent("Valid sources are: %s", + Arrays.toString(WarpMigrationSource.values()))); return true; } + WarpMigrator migrator = new WarpMigrator(source); + player.sendMessage(ChatHelper.getStandardComponent(true, "Migrating the warps...")); + migrator.migrate(player).whenComplete((result, throwable) -> + handleMigrationResult(player, result, throwable)); + return true; + } - // Combine the args to one warp name + private void handleMigrationResult(@NonNull Player player, @NonNull MigrationResult result, @Nullable Throwable throwable) { + if (throwable != null) { + player.sendMessage(ChatHelper.getErrorComponent("Something went wrong while migrating the warps: %s", + throwable.getMessage())); + return; + } + + if (!result.success()) { + player.sendMessage(ChatHelper.getErrorComponent("Migration failed: %s", result.errorMessage())); + return; + } + + if (result.migratedCount() == 0 && result.failedCount() == 0) { + player.sendMessage(ChatHelper.getStandardComponent(false, "No warps found to migrate.")); + } else if (result.failedCount() == 0) { + player.sendMessage(ChatHelper.getSuccessComponent("Successfully migrated %d warp(s)!", + result.migratedCount())); + } else if (result.migratedCount() == 0) { + player.sendMessage(ChatHelper.getErrorComponent("Failed to migrate all %d warp(s)!", result.failedCount())); + } else { + player.sendMessage(ChatHelper.getStandardComponent(false, "Migration completed: %d warp(s) migrated, %d failed.", + result.migratedCount(), result.failedCount())); + } + } + + private boolean handleWarpTeleport(@NonNull Player player, String @NonNull [] args) { String key = String.join(" ", args); if (!checkForWarpUsePermissionAndMessage(player)) return true; - // Find the warp with the given key Warp warp = NavigationModule.getInstance().getWarpsComponent().getWarpByName(key); if (warp == null) { - player.sendMessage(ChatHelper.getErrorString("The warp with the name %s does not exist in this team!", key)); + player.sendMessage(ChatHelper.getErrorComponent("The warp with the name %s does not exist in this team!", key)); return true; } NavigationModule.getInstance().getWarpsComponent().warpPlayer(player, warp); - return true; } private static boolean checkForWarpUsePermissionAndMessage(@NonNull 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")); + player.sendMessage(ChatHelper.getErrorComponent("You don't have the required %s to %s warps.", "permission", "use")); return false; } return true; diff --git a/src/main/java/net/buildtheearth/buildteamtools/modules/navigation/components/warps/menu/WarpEditMenu.java b/src/main/java/net/buildtheearth/buildteamtools/modules/navigation/components/warps/menu/WarpEditMenu.java index 07002b5..75c5daf 100644 --- a/src/main/java/net/buildtheearth/buildteamtools/modules/navigation/components/warps/menu/WarpEditMenu.java +++ b/src/main/java/net/buildtheearth/buildteamtools/modules/navigation/components/warps/menu/WarpEditMenu.java @@ -83,7 +83,9 @@ protected void setMenuItemsAsync() { // Set the location item if the warp already exists. Otherwise, the location is set automatically on creation. if (alreadyExists) { - ArrayList locationLore = ListUtil.createList("", "§eWorld: §7" + warp.getWorldName(), "§eLatitude: §7" + warp.getLat(), "§eLongitude: §7" + warp.getLon(), "§eElevation: §7" + warp.getY()); + ArrayList locationLore = ListUtil.createList("", "§eWorld: §7" + warp.getWorldName(), + "§eLatitude: §7" + warp.getCoordinate().latitude(), "§eLongitude: §7" + warp.getCoordinate().longitude(), + "§eElevation: §7" + warp.getY()); getMenu().getSlot(LOCATION_SLOT).setItem(Item.create(Objects.requireNonNull(XMaterial.COMPASS.get()), "§6§lChange Location", locationLore)); } @@ -144,7 +146,7 @@ protected void setItemClickEventsAsync() { GeographicalCoordinate coordinate = Projection.toGeo(new MinecraftCoordinate(location.getX(), location.getZ())); //Get the country belonging to the coordinates - CompletableFuture future = OpenStreetMapAPI.getCountryFromLocationAsync(new double[]{coordinate.latitude(), coordinate.longitude()}); + CompletableFuture future = OpenStreetMapAPI.getCountryFromLocationAsync(coordinate); future.thenAccept(result -> { String regionName = result[0]; @@ -161,8 +163,7 @@ protected void setItemClickEventsAsync() { warp.setCountryCode(countryCodeCCA2); warp.setWorldName(location.getWorld().getName()); warp.setY(location.getY()); - warp.setLat(coordinate.latitude()); - warp.setLon(coordinate.longitude()); + warp.setCoordinate(coordinate); warp.setYaw(location.getYaw()); warp.setPitch(location.getPitch()); diff --git a/src/main/java/net/buildtheearth/buildteamtools/modules/navigation/components/warps/menu/WarpMenu.java b/src/main/java/net/buildtheearth/buildteamtools/modules/navigation/components/warps/menu/WarpMenu.java index fdb0a24..b8a0432 100644 --- a/src/main/java/net/buildtheearth/buildteamtools/modules/navigation/components/warps/menu/WarpMenu.java +++ b/src/main/java/net/buildtheearth/buildteamtools/modules/navigation/components/warps/menu/WarpMenu.java @@ -12,6 +12,7 @@ import net.buildtheearth.buildteamtools.utils.heads.HeadFactory; import net.buildtheearth.buildteamtools.utils.heads.HeadTexture; import net.buildtheearth.buildteamtools.utils.menus.AbstractPaginatedMenu; +import net.buildtheearth.model.GeographicalCoordinate; import org.bukkit.entity.Player; import org.ipvp.canvas.mask.BinaryMask; import org.ipvp.canvas.mask.Mask; @@ -70,7 +71,8 @@ protected List getSource() { BuildTeam currentTeam = NetworkModule.getInstance().getBuildTeam(); if (getMenuPlayer().hasPermission(Permissions.WARP_CREATE) && currentTeam != null && currentTeam.equals(warpGroup.getBuildTeam())) - warps.add(new Warp(null, "%create-warp%", null, null, null, null, null, null, 0, 0, 0, 0, 0, false)); + warps.add(new Warp(null, "%create-warp%", null, null, null, null, null, null, new GeographicalCoordinate(0, 0), 0, + 0, 0, false)); return warps; } diff --git a/src/main/java/net/buildtheearth/buildteamtools/modules/navigation/components/warps/migrators/EssentialsWarpMigrator.java b/src/main/java/net/buildtheearth/buildteamtools/modules/navigation/components/warps/migrators/EssentialsWarpMigrator.java new file mode 100644 index 0000000..3e445d7 --- /dev/null +++ b/src/main/java/net/buildtheearth/buildteamtools/modules/navigation/components/warps/migrators/EssentialsWarpMigrator.java @@ -0,0 +1,66 @@ +package net.buildtheearth.buildteamtools.modules.navigation.components.warps.migrators; + +import com.alpsbte.alpslib.utils.ChatHelper; +import com.earth2me.essentials.Essentials; +import com.earth2me.essentials.Warps; +import net.buildtheearth.buildteamtools.modules.navigation.NavUtils; +import net.buildtheearth.buildteamtools.modules.navigation.components.warps.WarpsComponent; +import net.buildtheearth.buildteamtools.modules.navigation.components.warps.model.MigrationResult; +import net.buildtheearth.buildteamtools.modules.navigation.components.warps.model.WarpGroup; +import net.buildtheearth.buildteamtools.modules.network.NetworkModule; +import org.bukkit.entity.Player; +import org.bukkit.plugin.java.JavaPlugin; + +import java.util.Objects; +import java.util.concurrent.CompletableFuture; + +public class EssentialsWarpMigrator implements IWarpMigrator { + @Override + public CompletableFuture migrate(Player player) { + return CompletableFuture.supplyAsync(() -> { + int migratedCount = 0; + int failedCount = 0; + + try { + Essentials essentials = JavaPlugin.getPlugin(Essentials.class); + Warps warps = essentials.getWarps(); + + for (String warp : warps.getList()) { + if (migrateIndividualWarp(warps, warp, player)) { + migratedCount++; + } else { + failedCount++; + } + } + + return MigrationResult.success(migratedCount, failedCount); + } catch (Exception e) { + ChatHelper.logError( + """ + An error occurred while migrating the essentials warps! (this probably means essentials isn't /installed) + %s""", e); + return MigrationResult.failure("Failed to access Essentials warps. Is Essentials installed? Error: " + e.getMessage()); + } + }); + } + + private boolean migrateIndividualWarp(Warps warps, String warp, Player player) { + try { + WarpGroup group = + WarpsComponent.getOtherWarpGroup(Objects.requireNonNull(NetworkModule.getInstance().getBuildTeam()).getWarpGroups()); + if (group != null && group.getWarps().stream().anyMatch(w -> w.getName().equalsIgnoreCase(warp))) { + ChatHelper.sendErrorMessage(player, "Warp '%s' already exists in the database, skipping migration.", warp); + return false; + } + + if (group == null) { + group = NavUtils.createOtherWarpGroup(NetworkModule.getInstance().getBuildTeam()); + } + WarpsComponent.createWarp(warps.getWarp(warp), warp, group, player); + return true; + } catch (Exception e) { + ChatHelper.logError("Failed to migrate warp '%s': %s", warp, e.getMessage()); + return false; + } + } +} diff --git a/src/main/java/net/buildtheearth/buildteamtools/modules/navigation/components/warps/migrators/IWarpMigrator.java b/src/main/java/net/buildtheearth/buildteamtools/modules/navigation/components/warps/migrators/IWarpMigrator.java new file mode 100644 index 0000000..0d4d721 --- /dev/null +++ b/src/main/java/net/buildtheearth/buildteamtools/modules/navigation/components/warps/migrators/IWarpMigrator.java @@ -0,0 +1,10 @@ +package net.buildtheearth.buildteamtools.modules.navigation.components.warps.migrators; + +import net.buildtheearth.buildteamtools.modules.navigation.components.warps.model.MigrationResult; +import org.bukkit.entity.Player; + +import java.util.concurrent.CompletableFuture; + +public interface IWarpMigrator { + CompletableFuture migrate(Player player); +} diff --git a/src/main/java/net/buildtheearth/buildteamtools/modules/navigation/components/warps/model/MigrationResult.java b/src/main/java/net/buildtheearth/buildteamtools/modules/navigation/components/warps/model/MigrationResult.java new file mode 100644 index 0000000..9eb4397 --- /dev/null +++ b/src/main/java/net/buildtheearth/buildteamtools/modules/navigation/components/warps/model/MigrationResult.java @@ -0,0 +1,18 @@ +package net.buildtheearth.buildteamtools.modules.navigation.components.warps.model; + +import org.jetbrains.annotations.Contract; +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; + +public record MigrationResult(int migratedCount, int failedCount, boolean success, @Nullable String errorMessage) { + + @Contract("_, _ -> new") + public static @NonNull MigrationResult success(int migratedCount, int failedCount) { + return new MigrationResult(migratedCount, failedCount, true, null); + } + + @Contract("_ -> new") + public static @NonNull MigrationResult failure(String errorMessage) { + return new MigrationResult(0, 0, false, errorMessage); + } +} diff --git a/src/main/java/net/buildtheearth/buildteamtools/modules/navigation/components/warps/model/Warp.java b/src/main/java/net/buildtheearth/buildteamtools/modules/navigation/components/warps/model/Warp.java index a45da1e..f936cb4 100644 --- a/src/main/java/net/buildtheearth/buildteamtools/modules/navigation/components/warps/model/Warp.java +++ b/src/main/java/net/buildtheearth/buildteamtools/modules/navigation/components/warps/model/Warp.java @@ -8,6 +8,7 @@ import net.buildtheearth.buildteamtools.utils.Utils; import net.buildtheearth.buildteamtools.utils.heads.HeadFactory; import net.buildtheearth.buildteamtools.utils.heads.LetterType; +import net.buildtheearth.model.GeographicalCoordinate; import org.bukkit.Material; import org.bukkit.inventory.ItemStack; import org.json.JSONObject; @@ -55,11 +56,7 @@ public class Warp { @Getter @Setter - private double lat; - - @Getter - @Setter - private double lon; + private GeographicalCoordinate coordinate; @Getter @Setter @@ -81,10 +78,12 @@ public class Warp { * Create a warp with a random warp ID. */ public Warp(WarpGroup warpGroup, String name, String countryCode, String countryCodeType, String address, - AddressType addressType, String material, String worldName, double lat, double lon, double y, float yaw, + AddressType addressType, String material, String worldName, GeographicalCoordinate coordinate, double y, + float yaw, float pitch, boolean isHighlight) { - this(UUID.randomUUID(), warpGroup, name, countryCode, countryCodeType, address, addressType, material, worldName, lat, - lon, y, yaw, pitch, isHighlight); + this(UUID.randomUUID(), warpGroup, name, countryCode, countryCodeType, address, addressType, material, worldName, + coordinate, + y, yaw, pitch, isHighlight); } public ItemStack getMaterialItem() { @@ -131,8 +130,8 @@ public JSONObject toJSON() { json.put("countryCode", countryCode); json.put("countryCodeType", countryCodeType); json.put("worldName", worldName); - json.put("lat", lat); - json.put("lon", lon); + json.put("lat", coordinate.latitude()); + json.put("lon", coordinate.longitude()); json.put("y", y); json.put("yaw", yaw); json.put("pitch", pitch); diff --git a/src/main/java/net/buildtheearth/buildteamtools/modules/navigation/components/warps/model/WarpMigrationSource.java b/src/main/java/net/buildtheearth/buildteamtools/modules/navigation/components/warps/model/WarpMigrationSource.java new file mode 100644 index 0000000..300f781 --- /dev/null +++ b/src/main/java/net/buildtheearth/buildteamtools/modules/navigation/components/warps/model/WarpMigrationSource.java @@ -0,0 +1,25 @@ +package net.buildtheearth.buildteamtools.modules.navigation.components.warps.model; + +import lombok.Getter; + +import java.util.Arrays; + +@Getter +public enum WarpMigrationSource { + ESSENTIALS("essentials"); + + private final String[] validNames; + + WarpMigrationSource(String... validNames) { + this.validNames = validNames; + } + + public static WarpMigrationSource fromString(String name) { + for (WarpMigrationSource warpMigrationSource : WarpMigrationSource.values()) { + if (Arrays.asList(warpMigrationSource.validNames).contains(name)) { + return warpMigrationSource; + } + } + return null; + } +} diff --git a/src/main/java/net/buildtheearth/buildteamtools/modules/network/NetworkModule.java b/src/main/java/net/buildtheearth/buildteamtools/modules/network/NetworkModule.java index 036a3d8..fe97b6c 100644 --- a/src/main/java/net/buildtheearth/buildteamtools/modules/network/NetworkModule.java +++ b/src/main/java/net/buildtheearth/buildteamtools/modules/network/NetworkModule.java @@ -1,5 +1,6 @@ package net.buildtheearth.buildteamtools.modules.network; +import com.alpsbte.alpslib.utils.ChatHelper; import com.google.common.io.ByteArrayDataOutput; import com.google.common.io.ByteStreams; import lombok.Getter; @@ -181,6 +182,7 @@ public boolean ownsRegion(String regionName, String countryCodeCca2) { AtomicBoolean ownsRegion = new AtomicBoolean(false); if (buildTeam != null && buildTeam.getRegions() != null) { buildTeam.getRegions().forEach(region -> { + ChatHelper.logDebug("Checking region: %s against %s, %s", region.toString(), regionName, countryCodeCca2); if (region.getName().equals(regionName) || region.getCountryCodeCca2().equals(countryCodeCca2)) ownsRegion.set(true); }); diff --git a/src/main/java/net/buildtheearth/buildteamtools/modules/network/api/NetworkAPI.java b/src/main/java/net/buildtheearth/buildteamtools/modules/network/api/NetworkAPI.java index db04943..b3f5f3a 100644 --- a/src/main/java/net/buildtheearth/buildteamtools/modules/network/api/NetworkAPI.java +++ b/src/main/java/net/buildtheearth/buildteamtools/modules/network/api/NetworkAPI.java @@ -12,6 +12,7 @@ import net.buildtheearth.buildteamtools.modules.network.model.Region; import net.buildtheearth.buildteamtools.modules.network.model.RegionType; import net.buildtheearth.buildteamtools.utils.io.ConfigPaths; +import net.buildtheearth.model.GeographicalCoordinate; import okhttp3.MediaType; import okhttp3.RequestBody; import org.bukkit.Bukkit; @@ -160,7 +161,9 @@ public void onResponse(String response) { if (warpGroup == null) warpGroup = otherWarpGroup; - Warp warp = new Warp(warpID, warpGroup, warpName, countryCode, "cca3", address, addressType, material, warpWorldName, warpLat, warpLon, warpHeight, warpYaw, warpPitch, isHighlight); + Warp warp = new Warp(warpID, warpGroup, warpName, countryCode, "cca3", address, addressType, + material, warpWorldName, new GeographicalCoordinate(warpLat, warpLon), warpHeight, warpYaw, + warpPitch, isHighlight); // If the warp belongs to a warp group, add it to that, otherwise add it to the "other" warp group. boolean added = false; diff --git a/src/main/java/net/buildtheearth/buildteamtools/modules/network/api/OpenStreetMapAPI.java b/src/main/java/net/buildtheearth/buildteamtools/modules/network/api/OpenStreetMapAPI.java index 1ee892b..2754e40 100644 --- a/src/main/java/net/buildtheearth/buildteamtools/modules/network/api/OpenStreetMapAPI.java +++ b/src/main/java/net/buildtheearth/buildteamtools/modules/network/api/OpenStreetMapAPI.java @@ -1,6 +1,7 @@ package net.buildtheearth.buildteamtools.modules.network.api; import com.alpsbte.alpslib.utils.ChatHelper; +import net.buildtheearth.model.GeographicalCoordinate; import org.jetbrains.annotations.NotNull; import org.json.simple.JSONArray; import org.json.simple.JSONObject; @@ -14,9 +15,10 @@ 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 @NotNull CompletableFuture getCountryFromLocationAsync(double @NotNull [] coordinates) { + public static @NotNull CompletableFuture getCountryFromLocationAsync(@NotNull GeographicalCoordinate coordinates) { CompletableFuture future = new CompletableFuture<>(); - String url = "https://photon.komoot.io/reverse?lat=" + coordinates[0] + "&lon=" + coordinates[1] + "&lang=en"; + String url = "https://photon.komoot.io/reverse?lat=" + coordinates.latitude() + "&lon=" + coordinates.longitude() + + "&lang=en"; ChatHelper.logDebug("Requesting country from location: %s", url); diff --git a/src/main/java/net/buildtheearth/buildteamtools/modules/network/model/BuildTeam.java b/src/main/java/net/buildtheearth/buildteamtools/modules/network/model/BuildTeam.java index 9a4d663..b4dee7a 100644 --- a/src/main/java/net/buildtheearth/buildteamtools/modules/network/model/BuildTeam.java +++ b/src/main/java/net/buildtheearth/buildteamtools/modules/network/model/BuildTeam.java @@ -11,6 +11,7 @@ import net.buildtheearth.buildteamtools.modules.network.api.API; import net.buildtheearth.buildteamtools.modules.network.api.NetworkAPI; import org.bukkit.entity.Player; +import org.jspecify.annotations.NonNull; import java.io.IOException; import java.util.ArrayList; @@ -64,15 +65,19 @@ public BuildTeam(String ID, String serverIP, String name, String blankName, Stri this.IP = (!isConnected) ? serverIP : tag.toLowerCase() + ".buildtheearth.net"; } - public void createWarp(Player creator, Warp warp) { - // Check if the team owns that warp + public void createWarp(Player creator, @NonNull Warp warp) { + createWarp(creator, warp, false); + } + + public void createWarp(@NonNull Player creator, @NonNull Warp warp, boolean reducedMessages) { if (!warp.getWarpGroup().getBuildTeam().getID().equals(this.getID())) { - creator.sendMessage(ChatHelper.getErrorString("You can only create warps for your own team!")); + creator.sendMessage(ChatHelper.getErrorString("Could not create warp %s. You can only create warps for your own " + + "team! Id's have not matched", warp.getName())); return; } warp.getWarpGroup().getWarps().add(warp); - ChatHelper.sendSuccessfulMessage(creator, "Successfully created the warp %s!", warp.getName()); + if (!reducedMessages) ChatHelper.sendSuccessfulMessage(creator, "Successfully created the warp %s!", warp.getName()); NetworkAPI.createWarp(warp, new API.ApiResponseCallback() { @Override diff --git a/src/main/java/net/buildtheearth/buildteamtools/modules/network/model/Permissions.java b/src/main/java/net/buildtheearth/buildteamtools/modules/network/model/Permissions.java index d29ee74..92c5f57 100644 --- a/src/main/java/net/buildtheearth/buildteamtools/modules/network/model/Permissions.java +++ b/src/main/java/net/buildtheearth/buildteamtools/modules/network/model/Permissions.java @@ -20,6 +20,7 @@ public abstract class Permissions { public static final String WARP_CREATE = "btt.warp.create"; public static final String WARP_EDIT = "btt.warp.edit"; public static final String WARP_DELETE = "btt.warp.delete"; + public static final String WARP_MIGRATE = "btt.warp.migrate"; public static final String WARP_GROUP_CREATE = "btt.warp.group.create"; public static final String WARP_GROUP_EDIT = "btt.warp.group.edit";