diff --git a/pom.xml b/pom.xml index bdc7be2..48f9466 100644 --- a/pom.xml +++ b/pom.xml @@ -6,13 +6,13 @@ com.alpsbte PlotSystem-Terra - 2.1 + 3.0 - + - nachwahl-repo - https://maven.nachwahl.dev/ + alpsbte-repo + https://mvn.alps-bte.com/repository/alps-bte/ @@ -97,9 +97,9 @@ - com.jcraft + com.github.mwiede jsch - 0.1.55 + 0.2.0 compile diff --git a/src/main/java/com/alpsbte/plotsystemterra/PlotSystemTerra.java b/src/main/java/com/alpsbte/plotsystemterra/PlotSystemTerra.java index 263e060..35123a7 100644 --- a/src/main/java/com/alpsbte/plotsystemterra/PlotSystemTerra.java +++ b/src/main/java/com/alpsbte/plotsystemterra/PlotSystemTerra.java @@ -1,6 +1,7 @@ package com.alpsbte.plotsystemterra; import com.alpsbte.plotsystemterra.commands.CMD_CreatePlot; +import com.alpsbte.plotsystemterra.commands.CMD_PastePlot; import com.alpsbte.plotsystemterra.core.DatabaseConnection; import com.alpsbte.plotsystemterra.core.EventListener; import com.alpsbte.plotsystemterra.core.config.ConfigManager; @@ -26,15 +27,17 @@ public class PlotSystemTerra extends JavaPlugin { - private static final String VERSION = "2.1"; + private static final String VERSION = "3.0"; private static PlotSystemTerra plugin; private ConfigManager configManager; + private PlotPaster plotPaster; private boolean pluginEnabled = false; @Override public void onEnable() { + System.setProperty("org.apache.commons.logging.Log", "org.apache.commons.logging.impl.NoOpLog"); // Disable Logging plugin = this; String successPrefix = ChatColor.DARK_GRAY + "[" + ChatColor.DARK_GREEN + "✔" + ChatColor.DARK_GRAY + "] " + ChatColor.GRAY; @@ -97,6 +100,7 @@ public void onEnable() { // Register commands try { this.getCommand("createplot").setExecutor(new CMD_CreatePlot()); + this.getCommand("pasteplot").setExecutor(new CMD_PastePlot()); Bukkit.getConsoleSender().sendMessage(successPrefix + "Successfully registered commands."); } catch (Exception ex) { Bukkit.getConsoleSender().sendMessage(errorPrefix + "Could not register commands."); @@ -122,7 +126,8 @@ public void onEnable() { }); // Start checking for plots to paste - new PlotPaster().start(); + plotPaster = new PlotPaster(); + plotPaster.start(); pluginEnabled = true; Bukkit.getConsoleSender().sendMessage(" "); @@ -164,6 +169,10 @@ public static PlotSystemTerra getPlugin() { return plugin; } + public PlotPaster getPlotPaster() { + return plotPaster; + } + public static class DependencyManager { // List with all missing dependencies diff --git a/src/main/java/com/alpsbte/plotsystemterra/commands/CMD_PastePlot.java b/src/main/java/com/alpsbte/plotsystemterra/commands/CMD_PastePlot.java new file mode 100644 index 0000000..8674755 --- /dev/null +++ b/src/main/java/com/alpsbte/plotsystemterra/commands/CMD_PastePlot.java @@ -0,0 +1,59 @@ +package com.alpsbte.plotsystemterra.commands; + +import com.alpsbte.plotsystemterra.PlotSystemTerra; +import com.alpsbte.plotsystemterra.core.DatabaseConnection; +import com.alpsbte.plotsystemterra.core.plotsystem.CityProject; +import com.alpsbte.plotsystemterra.core.plotsystem.PlotPaster; +import com.alpsbte.plotsystemterra.utils.Utils; +import com.sk89q.worldedit.Vector; +import org.bukkit.Bukkit; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; + +import java.sql.ResultSet; +import java.util.logging.Level; + +public class CMD_PastePlot implements CommandExecutor { + @Override + public boolean onCommand(CommandSender sender, Command cmd, String s, String[] args) { + if(Utils.hasPermission(sender, "pasteplot")) { + try { + if (args.length >= 1 && Utils.tryParseInt(args[0]) != null) { + int plotID = Integer.parseInt(args[0]); + + try (ResultSet rs = DatabaseConnection.createStatement("SELECT status, city_project_id, mc_coordinates, version FROM plotsystem_plots WHERE id = ?") + .setValue(plotID).executeQuery()) { + + if (rs.next() && rs.getString(1).equals("completed")) { + PlotPaster plotPaster = PlotSystemTerra.getPlugin().getPlotPaster(); + + String[] splitCoordinates = rs.getString(3).split(","); + Vector mcCoordinates = Vector.toBlockPoint( + Float.parseFloat(splitCoordinates[0]), + Float.parseFloat(splitCoordinates[1]), + Float.parseFloat(splitCoordinates[2]) + ); + + try { + PlotPaster.pastePlotSchematic(plotID, new CityProject(rs.getInt(2)), plotPaster.world, mcCoordinates, rs.getDouble(4), plotPaster.fastMode); + Bukkit.broadcastMessage("§7§l>§a Pasted §61 §aplot!"); + } catch (Exception ex) { + Bukkit.getLogger().log(Level.SEVERE, "An error occurred while pasting plot with the ID " + plotID + "!", ex); + sender.sendMessage(Utils.getErrorMessageFormat("An error occurred while pasting plot!")); + } + } else sender.sendMessage(Utils.getErrorMessageFormat("Plot with the ID " + plotID + " is not completed!")); + + DatabaseConnection.closeResultSet(rs); + } + } else { + sender.sendMessage(Utils.getErrorMessageFormat("Incorrect Input! Try /pasteplot ")); + } + } catch (Exception ex) { + Bukkit.getLogger().log(Level.SEVERE, "An error occurred while pasting plot!", ex); + sender.sendMessage(Utils.getErrorMessageFormat("An error occurred while pasting plot!")); + } + } + return true; + } +} diff --git a/src/main/java/com/alpsbte/plotsystemterra/core/DatabaseConnection.java b/src/main/java/com/alpsbte/plotsystemterra/core/DatabaseConnection.java index 8dd1dd7..0985067 100644 --- a/src/main/java/com/alpsbte/plotsystemterra/core/DatabaseConnection.java +++ b/src/main/java/com/alpsbte/plotsystemterra/core/DatabaseConnection.java @@ -39,7 +39,6 @@ public static void InitializeDatabase() throws ClassNotFoundException { dataSource = new HikariDataSource(config); } - @Deprecated public static Connection getConnection() { int retries = 3; while (retries > 0) { @@ -73,31 +72,6 @@ public static void closeResultSet(ResultSet resultSet) throws SQLException { Bukkit.getLogger().log(Level.SEVERE, "There are multiple database connections opened. Please report this issue."); } - /** - * Returns a missing auto increment id - * @param table in the database - * @return smallest missing auto increment id in the table - */ - public static int getTableID(String table) { - try { - String query ="SELECT id + 1 available_id FROM $table t WHERE NOT EXISTS (SELECT * FROM $table WHERE $table.id = t.id + 1) ORDER BY id LIMIT 1" - .replace("$table", table); - try (ResultSet rs = DatabaseConnection.createStatement(query).executeQuery()) { - if (rs.next()) { - int i = rs.getInt(1); - DatabaseConnection.closeResultSet(rs); - return i; - } - - DatabaseConnection.closeResultSet(rs); - return 1; - } - } catch (SQLException ex) { - Bukkit.getLogger().log(Level.SEVERE, "A SQL error occurred!", ex); - return 1; - } - } - public static class StatementBuilder { private final String sql; private final List values = new ArrayList<>(); diff --git a/src/main/java/com/alpsbte/plotsystemterra/core/config/Config.java b/src/main/java/com/alpsbte/plotsystemterra/core/config/Config.java index 222cd8f..70913b6 100644 --- a/src/main/java/com/alpsbte/plotsystemterra/core/config/Config.java +++ b/src/main/java/com/alpsbte/plotsystemterra/core/config/Config.java @@ -16,7 +16,7 @@ public class Config extends YamlConfiguration { - public static final double VERSION = 1.3; + public static final double VERSION = 1.4; private final File file; private final String fileName; diff --git a/src/main/java/com/alpsbte/plotsystemterra/core/config/ConfigPaths.java b/src/main/java/com/alpsbte/plotsystemterra/core/config/ConfigPaths.java index 3b9061c..79fe606 100644 --- a/src/main/java/com/alpsbte/plotsystemterra/core/config/ConfigPaths.java +++ b/src/main/java/com/alpsbte/plotsystemterra/core/config/ConfigPaths.java @@ -15,6 +15,12 @@ public abstract class ConfigPaths { public static final String DATABASE_PASSWORD = DATABASE + "password"; + // PLOT SCANNING + private static final String ENVIRONMENT = "environment."; + public static final String ENVIRONMENT_ENABLED = ENVIRONMENT + "enabled"; + public static final String ENVIRONMENT_RADIUS = ENVIRONMENT + "radius"; + + // PLOT PASTING public static final String WORLD_NAME = "world-name"; public static final String FAST_MODE = "fast-mode"; diff --git a/src/main/java/com/alpsbte/plotsystemterra/core/plotsystem/CreatePlotMenu.java b/src/main/java/com/alpsbte/plotsystemterra/core/plotsystem/CreatePlotMenu.java index cc83534..d83502c 100644 --- a/src/main/java/com/alpsbte/plotsystemterra/core/plotsystem/CreatePlotMenu.java +++ b/src/main/java/com/alpsbte/plotsystemterra/core/plotsystem/CreatePlotMenu.java @@ -99,7 +99,7 @@ public Menu getDifficultyMenu() { ); difficultyMenu.getSlot(10).setClickHandler((clickPlayer, clickInformation) -> { clickPlayer.closeInventory(); - CompletableFuture.supplyAsync(() -> PlotCreator.Create(clickPlayer, cityProject, 1)); + PlotCreator.Create(clickPlayer, cityProject, 1); }); difficultyMenu.getSlot(13).setItem( @@ -109,7 +109,7 @@ public Menu getDifficultyMenu() { ); difficultyMenu.getSlot(13).setClickHandler((clickPlayer, clickInformation) -> { clickPlayer.closeInventory(); - CompletableFuture.supplyAsync(() -> PlotCreator.Create(clickPlayer, cityProject, 2)); + PlotCreator.Create(clickPlayer, cityProject, 2); }); difficultyMenu.getSlot(16).setItem( @@ -119,7 +119,7 @@ public Menu getDifficultyMenu() { ); difficultyMenu.getSlot(16).setClickHandler((clickPlayer, clickInformation) -> { clickPlayer.closeInventory(); - CompletableFuture.supplyAsync(() -> PlotCreator.Create(clickPlayer, cityProject, 3)); + PlotCreator.Create(clickPlayer, cityProject, 3); }); return difficultyMenu; diff --git a/src/main/java/com/alpsbte/plotsystemterra/core/plotsystem/PlotCreator.java b/src/main/java/com/alpsbte/plotsystemterra/core/plotsystem/PlotCreator.java index 7648252..a6dac55 100644 --- a/src/main/java/com/alpsbte/plotsystemterra/core/plotsystem/PlotCreator.java +++ b/src/main/java/com/alpsbte/plotsystemterra/core/plotsystem/PlotCreator.java @@ -2,215 +2,322 @@ import com.alpsbte.plotsystemterra.PlotSystemTerra; import com.alpsbte.plotsystemterra.core.DatabaseConnection; +import com.alpsbte.plotsystemterra.core.config.ConfigPaths; import com.alpsbte.plotsystemterra.utils.FTPManager; import com.alpsbte.plotsystemterra.utils.Utils; import com.sk89q.worldedit.*; import com.sk89q.worldedit.Vector; -import com.sk89q.worldedit.bukkit.WorldEditPlugin; import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard; -import com.sk89q.worldedit.extent.clipboard.Clipboard; import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormat; import com.sk89q.worldedit.extent.clipboard.io.ClipboardWriter; import com.sk89q.worldedit.function.operation.ForwardExtentCopy; import com.sk89q.worldedit.function.operation.Operations; +import com.sk89q.worldedit.regions.AbstractRegion; +import com.sk89q.worldedit.regions.CylinderRegion; import com.sk89q.worldedit.regions.Polygonal2DRegion; import com.sk89q.worldedit.regions.Region; import org.apache.commons.lang.StringUtils; -import org.apache.commons.vfs2.FileSystemException; import org.bukkit.*; import org.bukkit.Location; import org.bukkit.block.Block; import org.bukkit.block.BlockFace; import org.bukkit.block.Sign; +import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.entity.Player; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; -import java.net.URISyntaxException; import java.nio.file.Files; import java.nio.file.Paths; -import java.sql.SQLException; +import java.sql.*; import java.time.LocalDate; import java.util.*; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicInteger; import java.util.logging.Level; public class PlotCreator { - + public final static double PLOT_VERSION = 3.0; public final static String schematicsPath = Paths.get(PlotSystemTerra.getPlugin().getDataFolder().getAbsolutePath(), "schematics") + File.separator; + public final static int MIN_OFFSET_Y = 5; - public static CompletableFuture Create(Player player, CityProject cityProject, int difficultyID) { - int plotID; - Region plotRegion; - - // Get WorldEdit selection of player - try { - plotRegion = Objects.requireNonNull(WorldEdit.getInstance().getSessionManager().findByName(player.getName())).getSelection( - Objects.requireNonNull(WorldEdit.getInstance().getSessionManager().findByName(player.getName())).getSelectionWorld()); - } catch (NullPointerException | IncompleteRegionException ex) { - player.sendMessage(Utils.getErrorMessageFormat("Please select a plot using WorldEdit!")); - return CompletableFuture.completedFuture(null); - } + public static void Create(Player player, CityProject cityProject, int difficultyID) { + CompletableFuture.runAsync(() -> { + int plotID; + boolean environmentEnabled; - // Conversion - Polygonal2DRegion polyRegion; - try { - // Check if WorldEdit selection is polygonal - if (plotRegion instanceof Polygonal2DRegion) { - // Cast WorldEdit region to polygonal region - polyRegion = (Polygonal2DRegion) plotRegion; - - if (polyRegion.getLength() > 100 || polyRegion.getWidth() > 100 || polyRegion.getHeight() > 250) { - player.sendMessage(Utils.getErrorMessageFormat("Please adjust your selection size!")); - return CompletableFuture.completedFuture(null); - } + Polygonal2DRegion plotRegion; + String plotFilePath; + Vector plotCenter; + CylinderRegion environmentRegion = null; + String environmentFilePath = null; + + Connection connection = null; - // Set minimum selection height under player location - if (polyRegion.getMinimumY() > player.getLocation().getY() - 5) { - polyRegion.setMinimumY((int) player.getLocation().getY() - 5); + try { + // Read the config + FileConfiguration config = PlotSystemTerra.getPlugin().getConfig(); + environmentEnabled = config.getBoolean(ConfigPaths.ENVIRONMENT_ENABLED); + int environmentRadius = config.getInt(ConfigPaths.ENVIRONMENT_RADIUS); + + + // Get WorldEdit selection of player + Region rawPlotRegion; + try { + rawPlotRegion = Objects.requireNonNull(WorldEdit.getInstance().getSessionManager().findByName(player.getName())).getSelection( + Objects.requireNonNull(WorldEdit.getInstance().getSessionManager().findByName(player.getName())).getSelectionWorld()); + } catch (NullPointerException | IncompleteRegionException ex) { + player.sendMessage(Utils.getErrorMessageFormat("Please select a plot using WorldEdit!")); + return; } - if (polyRegion.getMaximumY() <= player.getLocation().getY() + 1) { - polyRegion.setMaximumY((int) player.getLocation().getY() + 1); + + // Create plot and environment regions + // Check if WorldEdit selection is polygonal + if (rawPlotRegion instanceof Polygonal2DRegion) { + // Cast WorldEdit region to polygonal region + plotRegion = (Polygonal2DRegion) rawPlotRegion; + + // Check if the polygonal region is valid + if (plotRegion.getLength() > 100 || plotRegion.getWidth() > 100 || (plotRegion.getHeight() > 256 - MIN_OFFSET_Y)) { + player.sendMessage(Utils.getErrorMessageFormat("Please adjust your selection size!")); + return; + } + + // Get plot minY and maxY + double offsetHeight = (256 - plotRegion.getHeight()) / 2d; + final int minYOffset = plotRegion.getMinimumY() - (int) Math.ceil(offsetHeight); + final int maxYOffset = plotRegion.getMaximumY() + (int) Math.floor(offsetHeight); + final int minY = plotRegion.getMinimumY() - MIN_OFFSET_Y; + final int maxY = maxYOffset + (int) Math.ceil(offsetHeight) - MIN_OFFSET_Y; + + plotRegion.setMinimumY(minY); + plotRegion.setMaximumY(maxY); + + // Create the environment selection + if (environmentEnabled) { + // Get min region size for environment radius + int radius = Math.max(plotRegion.getWidth() / 2 + environmentRadius, plotRegion.getLength() / 2 + environmentRadius); + + // Create a new cylinder region with the size of the plot + the configured radius around it + Vector plotRegionCenter = plotRegion.getCenter(); + environmentRegion = new CylinderRegion( + plotRegion.getWorld(), + new Vector(Math.floor(plotRegionCenter.getX()), plotRegionCenter.getY(), Math.floor(plotRegionCenter.getZ())), + new Vector2D(radius, radius), + minY, + maxY + ); + + // Convert environment region to polygonal region and save points + final List environmentRegionPoints = environmentRegion.polygonize(-1); + final AtomicInteger newYMin = new AtomicInteger(minY); + + // Iterate over the points and check for the lowest Y value + final World world = player.getWorld(); + environmentRegionPoints.forEach(p -> { + int highestBlock = minYOffset; + for (int y = minYOffset; y <= maxYOffset; y++) { + if (world.getBlockAt(p.getBlockX(), y, p.getBlockZ()).getType() != Material.AIR) highestBlock = y; + } + if (highestBlock < newYMin.get()) newYMin.set(highestBlock); + }); + + // Update plot and environment min and max Y to new value if necessary + if (newYMin.get() < minY) { + int heightDif = (minY - newYMin.get()) + MIN_OFFSET_Y; + plotRegion.setMinimumY(newYMin.get() - MIN_OFFSET_Y); + environmentRegion.setMinimumY(newYMin.get() - MIN_OFFSET_Y); + plotRegion.setMaximumY(maxY - heightDif); + environmentRegion.setMaximumY(maxY - heightDif); + } + } + plotCenter = plotRegion.getCenter(); + } else { + player.sendMessage(Utils.getErrorMessageFormat("Please use polygonal selection to create a new plot!")); + return; } + // Check if selection contains sign - try { - if(!containsSign(polyRegion, player.getWorld())) { - player.sendMessage(Utils.getErrorMessageFormat("Please place a minimum of one sign for the street side.")); - return CompletableFuture.completedFuture(null); - } - } catch (Exception ex) { - Bukkit.getLogger().log(Level.SEVERE, "An error occurred while checking for sign(s) in selection!", ex); + if (!containsSign(plotRegion, player.getWorld())) { + player.sendMessage(Utils.getErrorMessageFormat("Please place a minimum of one sign for the street side!")); + return; } - } else { - player.sendMessage(Utils.getErrorMessageFormat("Please use poly selection to create a new plot!")); - return CompletableFuture.completedFuture(null); - } - } catch (Exception ex) { - Bukkit.getLogger().log(Level.SEVERE, "An error occurred while creating a new plot!", ex); - player.sendMessage(Utils.getErrorMessageFormat("An error occurred while creating plot!")); - return CompletableFuture.completedFuture(null); - } - // Convert polygon outline data to string - String polyOutline; - List points = new ArrayList<>(); - for(BlockVector2D point : polyRegion.getPoints()) - points.add(point.getBlockX() + "," + point.getBlockZ()); - polyOutline = StringUtils.join(points, "|"); + // Inform player about the plot creation + player.sendMessage(Utils.getInfoMessageFormat("Creating plot...")); - player.sendMessage(Utils.getInfoMessageFormat("Creating plot...")); + // Convert polygon outline data to string + String polyOutline; + List points = new ArrayList<>(); - // Saving schematic - String filePath; - try { - plotID = DatabaseConnection.getTableID("plotsystem_plots"); + for (BlockVector2D point : plotRegion.getPoints()) + points.add(point.getX() + "," + point.getZ()); + polyOutline = StringUtils.join(points, "|"); - filePath = Paths.get(schematicsPath, String.valueOf(cityProject.getServerID()), String.valueOf(cityProject.getID()), plotID + ".schematic").toString(); - File schematic = new File(filePath); - Files.deleteIfExists(schematic.getAbsoluteFile().toPath()); - boolean createdDirs = schematic.getParentFile().mkdirs(); - boolean createdFile = schematic.createNewFile(); - if ((!schematic.getParentFile().exists() && !createdDirs) || (!schematic.exists() && !createdFile)) { - Bukkit.getLogger().log(Level.SEVERE, "Could not create schematic file! (" + filePath + ")"); - player.sendMessage(Utils.getErrorMessageFormat("An error occurred while creating plot!")); - return CompletableFuture.completedFuture(null); - } + // Insert into database + connection = DatabaseConnection.getConnection(); - WorldEditPlugin worldEdit = PlotSystemTerra.DependencyManager.getWorldEditPlugin(); + if (connection != null) { + connection.setAutoCommit(false); - Clipboard cb = new BlockArrayClipboard(polyRegion); - cb.setOrigin(polyRegion.getCenter()); - LocalSession playerSession = PlotSystemTerra.DependencyManager.getWorldEdit().getSessionManager().findByName(player.getName()); - ForwardExtentCopy copy = new ForwardExtentCopy(playerSession.createEditSession(worldEdit.wrapPlayer(player)), polyRegion, cb, polyRegion.getMinimumPoint()); - Operations.completeLegacy(copy); + try (PreparedStatement stmt = Objects.requireNonNull(connection).prepareStatement("INSERT INTO plotsystem_plots (city_project_id, difficulty_id, mc_coordinates, outline, create_date, create_player, version) VALUES (?, ?, ?, ?, ?, ?, ?)", Statement.RETURN_GENERATED_KEYS)) { + stmt.setInt(1, cityProject.getID()); + stmt.setInt(2, difficultyID); + stmt.setString(3, plotCenter.getX() + "," + plotCenter.getY() + "," + plotCenter.getZ()); + stmt.setString(4, polyOutline); + stmt.setDate(5, java.sql.Date.valueOf(LocalDate.now())); + stmt.setString(6, player.getUniqueId().toString()); + stmt.setDouble(7, PLOT_VERSION); + stmt.executeUpdate(); - try (ClipboardWriter writer = ClipboardFormat.SCHEMATIC.getWriter(new FileOutputStream(schematic, false))) { - writer.write(cb, polyRegion.getWorld().getWorldData()); - } - } catch (Exception ex) { - Bukkit.getLogger().log(Level.SEVERE, "An error occurred while saving new plot to a schematic!", ex); - player.sendMessage(Utils.getErrorMessageFormat("An error occurred while creating plot!")); - return CompletableFuture.completedFuture(null); - } + // Get the id of the new plot + try (ResultSet rs = stmt.getGeneratedKeys()) { + if (rs.next()) { + plotID = rs.getInt(1); + } else throw new SQLException("Could not obtain generated key"); + } + } + } else throw new SQLException("Could not connect to database"); + + + // Save plot and environment regions to schematic files + // Get plot schematic file path + plotFilePath = createPlotSchematic(plotRegion, plotID + "", cityProject); + + if (plotFilePath == null) { + Bukkit.getLogger().log(Level.SEVERE, "Could not create plot schematic file!"); + player.sendMessage(Utils.getErrorMessageFormat("An error occurred while creating plot!")); + return; + } + + // Get environment schematic file path + if (environmentEnabled) { + environmentFilePath = createPlotSchematic(environmentRegion, plotID + "-env", cityProject); - // Save to database - try { - DatabaseConnection.createStatement("INSERT INTO plotsystem_plots (id, city_project_id, difficulty_id, mc_coordinates, outline, create_date, create_player) VALUES (?, ?, ?, ?, ?, ?, ?)") - .setValue(plotID) - .setValue(cityProject.getID()) - .setValue(difficultyID) - .setValue(polyRegion.getCenter().getX() + "," + polyRegion.getCenter().getY() + "," + polyRegion.getCenter().getZ()) - .setValue(polyOutline) - .setValue(java.sql.Date.valueOf(LocalDate.now())) - .setValue(player.getUniqueId().toString()).executeUpdate(); - - // Upload to SFTP or FTP server if enabled - FTPConfiguration ftpConfiguration = cityProject.getFTPConfiguration(); - if (ftpConfiguration != null) { - if (CompletableFuture.supplyAsync(() -> { - try { - return FTPManager.uploadSchematic(FTPManager.getFTPUrl(ftpConfiguration, cityProject.getID()), new File(filePath)); - } catch (FileSystemException | URISyntaxException ex) { - Bukkit.getLogger().log(Level.SEVERE, "An error occurred while uploading schematic file to SFTP/FTP server!", ex); - return null; + if (environmentFilePath == null) { + Bukkit.getLogger().log(Level.SEVERE, "Could not create environment schematic file!"); + player.sendMessage(Utils.getErrorMessageFormat("An error occurred while creating plot!")); + return; } - }).join() == null) throw new IOException(); - } + } - player.sendMessage(Utils.getInfoMessageFormat("Successfully created new plot! §f(City: §6" + cityProject.getName() + " §f| Plot-ID: §6" + plotID + "§f)")); - player.playSound(player.getLocation(), Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 1f, 1f); - try { + // Upload schematic files to SFTP/FTP server if enabled + FTPConfiguration ftpConfiguration = cityProject.getFTPConfiguration(); + if (ftpConfiguration != null) { + if (environmentEnabled) FTPManager.uploadSchematics(FTPManager.getFTPUrl(ftpConfiguration, cityProject.getID()), new File(plotFilePath), new File(environmentFilePath)); + else FTPManager.uploadSchematics(FTPManager.getFTPUrl(ftpConfiguration, cityProject.getID()), new File(plotFilePath)); + } + + + // Place plot markings on plot region placePlotMarker(plotRegion, player, plotID); + // TODO: Change top blocks of the plot region to mark plot as created + + + // Finalize database transaction + connection.commit(); + connection.close(); + + player.sendMessage(Utils.getInfoMessageFormat("Successfully created new plot! §f(City: §6" + cityProject.getName() + " §f| Plot-ID: §6" + plotID + "§f)")); + player.playSound(player.getLocation(), Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 1f, 1f); } catch (Exception ex) { - Bukkit.getLogger().log(Level.SEVERE, "An error occurred while placing plot marker!", ex); + try { + if (connection != null) { + connection.rollback(); + connection.close(); + } + } catch (SQLException sqlEx) { + Bukkit.getLogger().log(Level.SEVERE, "A SQL error occurred!", sqlEx); + } + Bukkit.getLogger().log(Level.SEVERE, "An error occurred while creating plot!", ex); + player.sendMessage(Utils.getErrorMessageFormat("An error occurred while creating plot!")); } - } catch (SQLException | IOException ex) { - Bukkit.getLogger().log(Level.SEVERE, "An error occurred while saving new plot to database!", ex); - player.sendMessage(Utils.getErrorMessageFormat("An error occurred while creating plot!")); + }); + } - try { - Files.deleteIfExists(Paths.get(filePath)); - } catch (IOException e) { - Bukkit.getLogger().log(Level.SEVERE, "An error occurred while deleting schematic!", ex); - } + + + + /** + * Creates a plot schematic of a selected region from the player. + * + * @param region: Selected poly region of the player + * @return the file path of the created schematic + */ + private static String createPlotSchematic(AbstractRegion region, String filename, CityProject cityProject) throws SQLException, IOException, WorldEditException { + // Create File + String filePath = Paths.get(schematicsPath, String.valueOf(cityProject.getServerID()), String.valueOf(cityProject.getID()), filename + ".schematic").toString(); + File schematic = new File(filePath); + + // Delete file if exists + Files.deleteIfExists(schematic.getAbsoluteFile().toPath()); + boolean createdDirs = schematic.getParentFile().mkdirs(); + boolean createdFile = schematic.createNewFile(); + + if ((!schematic.getParentFile().exists() && !createdDirs) || (!schematic.exists() && !createdFile)) + return null; + + + // Store content of region in schematic + BlockArrayClipboard cb = new BlockArrayClipboard(region); + cb.setOrigin(new Vector(region.getCenter().getX(), region.getMinimumPoint().getY(), region.getCenter().getZ())); + EditSession editSession = PlotSystemTerra.DependencyManager.getWorldEdit().getEditSessionFactory().getEditSession(region.getWorld(), -1); + ForwardExtentCopy forwardExtentCopy = new ForwardExtentCopy(editSession, region, cb, region.getMinimumPoint()); + Operations.complete(forwardExtentCopy); + + try(ClipboardWriter writer = ClipboardFormat.SCHEMATIC.getWriter(new FileOutputStream(schematic, false))) { + writer.write(cb, Objects.requireNonNull(region.getWorld()).getWorldData()); } - return CompletableFuture.completedFuture(null); + + return filePath; } + /** + * Checks if polygon region contains a sign and update sign text + * @param polyRegion WorldEdit region + * @param world Region world + * @return true if polygon region contains a sign, false otherwise + */ private static boolean containsSign(Polygonal2DRegion polyRegion, World world) { boolean hasSign = false; - for (int i = polyRegion.getMinimumPoint().getBlockX(); i <= polyRegion.getMaximumPoint().getBlockX(); i++) { - for (int j = polyRegion.getMinimumPoint().getBlockY(); j <= polyRegion.getMaximumPoint().getBlockY(); j++) { - for (int k = polyRegion.getMinimumPoint().getBlockZ(); k <= polyRegion.getMaximumPoint().getBlockZ(); k++) { - if (polyRegion.contains(new Vector(i, j, k))) { - Block block = world.getBlockAt(i, j, k); - if(block.getType().equals(Material.SIGN_POST) || block.getType().equals(Material.WALL_SIGN)) { - hasSign = true; - - Sign sign = (Sign) block.getState(); - for (int s = 0; s < 4; s++) { - if(s == 1) { - sign.setLine(s, "§c§lStreet Side"); - } else { - sign.setLine(s, ""); - } + for (int i = polyRegion.getMinimumPoint().getBlockX(); i <= polyRegion.getMaximumPoint().getBlockX(); i++) { + for (int j = polyRegion.getMinimumPoint().getBlockY(); j <= polyRegion.getMaximumPoint().getBlockY(); j++) { + for (int k = polyRegion.getMinimumPoint().getBlockZ(); k <= polyRegion.getMaximumPoint().getBlockZ(); k++) { + if (polyRegion.contains(new Vector(i, j, k))) { + Block block = world.getBlockAt(i, j, k); + if(block.getType().equals(Material.SIGN_POST) || block.getType().equals(Material.WALL_SIGN)) { + hasSign = true; + + Sign sign = (Sign) block.getState(); + for (int s = 0; s < 4; s++) { + if(s == 1) { + sign.setLine(s, "§c§lStreet Side"); + } else { + sign.setLine(s, ""); } - sign.update(); } + sign.update(); } } } } + } return hasSign; } + /** + * Places a plot marker in the center of the polygon region + * @param plotRegion WorldEdit region + * @param player Player + * @param plotID Plot ID + */ private static void placePlotMarker(Region plotRegion, Player player, int plotID) { Vector centerBlock = plotRegion.getCenter(); Location highestBlock = player.getWorld().getHighestBlockAt(centerBlock.getBlockX(), centerBlock.getBlockZ()).getLocation(); @@ -231,6 +338,11 @@ private static void placePlotMarker(Region plotRegion, Player player, int plotID }); } + /** + * Gets the direction the player is facing + * @param player Player + * @return Direction + */ private static BlockFace getPlayerFaceDirection(Player player) { float y = player.getLocation().getYaw(); if( y < 0 ){y += 360;} diff --git a/src/main/java/com/alpsbte/plotsystemterra/core/plotsystem/PlotPaster.java b/src/main/java/com/alpsbte/plotsystemterra/core/plotsystem/PlotPaster.java index 96ea88f..6410226 100644 --- a/src/main/java/com/alpsbte/plotsystemterra/core/plotsystem/PlotPaster.java +++ b/src/main/java/com/alpsbte/plotsystemterra/core/plotsystem/PlotPaster.java @@ -4,14 +4,13 @@ import com.alpsbte.plotsystemterra.core.DatabaseConnection; import com.alpsbte.plotsystemterra.core.config.ConfigPaths; import com.alpsbte.plotsystemterra.utils.FTPManager; -import com.sk89q.worldedit.CuboidClipboard; -import com.sk89q.worldedit.EditSession; -import com.sk89q.worldedit.MaxChangedBlocksException; -import com.sk89q.worldedit.Vector; +import com.sk89q.worldedit.*; import com.sk89q.worldedit.bukkit.BukkitWorld; -import com.sk89q.worldedit.schematic.SchematicFormat; -import com.sk89q.worldedit.world.DataException; -import org.apache.commons.vfs2.FileSystemException; +import com.sk89q.worldedit.extent.clipboard.Clipboard; +import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormat; +import com.sk89q.worldedit.function.operation.Operation; +import com.sk89q.worldedit.function.operation.Operations; +import com.sk89q.worldedit.session.ClipboardHolder; import org.bukkit.Bukkit; import org.bukkit.World; import org.bukkit.configuration.file.FileConfiguration; @@ -23,16 +22,15 @@ import java.nio.file.Paths; import java.sql.ResultSet; import java.sql.SQLException; -import java.util.concurrent.CompletableFuture; import java.util.logging.Level; public class PlotPaster extends Thread { private final String serverName; - private final boolean fastMode; + public final boolean fastMode; private final int pasteInterval; - private final World world; + public final World world; private final boolean broadcastMessages; public PlotPaster() { @@ -48,7 +46,7 @@ public PlotPaster() { @Override public void run() { Bukkit.getScheduler().scheduleSyncRepeatingTask(PlotSystemTerra.getPlugin(), () -> { - try (ResultSet rs = DatabaseConnection.createStatement("SELECT id, city_project_id, mc_coordinates FROM plotsystem_plots WHERE status = 'completed' AND pasted = '0' LIMIT 20") + try (ResultSet rs = DatabaseConnection.createStatement("SELECT id, city_project_id, mc_coordinates, version FROM plotsystem_plots WHERE status = 'completed' AND pasted = '0' LIMIT 20") .executeQuery()) { int pastedPlots = 0; @@ -73,7 +71,10 @@ public void run() { Float.parseFloat(splitCoordinates[2]) ); - pastePlotSchematic(plotID, city, world, mcCoordinates, fastMode); + double version = rs.getDouble(4); + if (rs.wasNull()) { version = 2; } + + pastePlotSchematic(plotID, city, world, mcCoordinates, version , fastMode); pastedPlots++; } } @@ -99,38 +100,41 @@ public void run() { }, 0L, 20L * pasteInterval); } - public static void pastePlotSchematic(int plotID, CityProject city, World world, Vector mcCoordinates, boolean fastMode) throws IOException, DataException, MaxChangedBlocksException, SQLException { - File file = Paths.get(PlotCreator.schematicsPath, String.valueOf(city.getServerID()), "finishedSchematics", String.valueOf(city.getID()), plotID + ".schematic").toFile(); + public static void pastePlotSchematic(int plotID, CityProject city, World world, Vector mcCoordinates, double plotVersion, boolean fastMode) throws IOException, WorldEditException, SQLException, URISyntaxException { + File outlineSchematic = Paths.get(PlotCreator.schematicsPath, String.valueOf(city.getServerID()), String.valueOf(city.getID()), plotID + ".schematic").toFile(); + File completedSchematic = Paths.get(PlotCreator.schematicsPath, String.valueOf(city.getServerID()), "finishedSchematics", String.valueOf(city.getID()), plotID + ".schematic").toFile(); // Download from SFTP or FTP server if enabled FTPConfiguration ftpConfiguration = city.getFTPConfiguration(); if (ftpConfiguration != null) { - Files.deleteIfExists(file.toPath()); - if (CompletableFuture.supplyAsync(() -> { - try { - return FTPManager.downloadSchematic(FTPManager.getFTPUrl(ftpConfiguration, city.getID()), file); - } catch (FileSystemException | URISyntaxException ex) { - Bukkit.getLogger().log(Level.SEVERE, "An error occurred while downloading schematic file from SFTP/FTP server!", ex); - return null; - } - }).join() == null) throw new IOException(); + Files.deleteIfExists(completedSchematic.toPath()); + FTPManager.downloadSchematic(FTPManager.getFTPUrl(ftpConfiguration, city.getID()), completedSchematic); } - if (file.exists()) { - EditSession editSession = new EditSession(new BukkitWorld(world), -1); + if (outlineSchematic.exists() && completedSchematic.exists()) { + com.sk89q.worldedit.world.World weWorld = new BukkitWorld(world); + EditSession editSession = WorldEdit.getInstance().getEditSessionFactory().getEditSession(weWorld, -1); if (fastMode) editSession.setFastMode(true); editSession.enableQueue(); - SchematicFormat schematicFormat = SchematicFormat.getFormat(file); - CuboidClipboard clipboard = schematicFormat.load(file); + Clipboard outlineClipboard = ClipboardFormat.SCHEMATIC.getReader(Files.newInputStream(outlineSchematic.toPath())).read(weWorld.getWorldData()); + Clipboard completedClipboard = ClipboardFormat.SCHEMATIC.getReader(Files.newInputStream(completedSchematic.toPath())).read(weWorld.getWorldData()); + + Vector toPaste; + if (plotVersion >= 3) { + Vector plotOriginOutline = outlineClipboard.getOrigin(); + toPaste = new Vector(plotOriginOutline.getX(), plotOriginOutline.getY(), plotOriginOutline.getZ()); + } else toPaste = mcCoordinates; - clipboard.paste(editSession, mcCoordinates, true); + Operation operation = new ClipboardHolder(completedClipboard, weWorld.getWorldData()).createPaste(editSession, weWorld.getWorldData()) + .to(toPaste).ignoreAirBlocks(true).build(); + Operations.complete(operation); editSession.flushQueue(); DatabaseConnection.createStatement("UPDATE plotsystem_plots SET pasted = '1' WHERE id = ?") .setValue(plotID).executeUpdate(); } else { - Bukkit.getLogger().log(Level.WARNING, "Could not find finished schematic file of plot #" + plotID + "!"); + Bukkit.getLogger().log(Level.WARNING, "Could not find schematic file(s) of plot #" + plotID + "!"); } } } diff --git a/src/main/java/com/alpsbte/plotsystemterra/utils/FTPManager.java b/src/main/java/com/alpsbte/plotsystemterra/utils/FTPManager.java index ec3a346..7d15dad 100644 --- a/src/main/java/com/alpsbte/plotsystemterra/utils/FTPManager.java +++ b/src/main/java/com/alpsbte/plotsystemterra/utils/FTPManager.java @@ -10,10 +10,10 @@ import java.io.File; import java.net.URI; import java.net.URISyntaxException; -import java.util.concurrent.CompletableFuture; import java.util.logging.Level; public class FTPManager { + private static FileSystemOptions fileOptions; private final static String DEFAULT_SCHEMATIC_PATH_LINUX = "/var/lib/Plot-System/schematics"; @@ -43,28 +43,29 @@ public static String getFTPUrl(FTPConfiguration ftpConfiguration, int cityID) th null).toString(); } - public static CompletableFuture uploadSchematic(String ftpURL, File schematic) throws FileSystemException { + public static void uploadSchematics(String ftpURL, File... schematics) throws FileSystemException { try (StandardFileSystemManager fileManager = new StandardFileSystemManager()) { fileManager.init(); - // Get local schematic - FileObject localSchematic = fileManager.toFileObject(schematic); + for (File schematic : schematics) { + // Get local schematic + FileObject localSchematic = fileManager.toFileObject(schematic); - // Get remote path and create missing directories - FileObject remote = fileManager.resolveFile(ftpURL.replace("finishedSchematics/", ""), fileOptions); - remote.createFolder(); + // Get remote path and create missing directories + FileObject remote = fileManager.resolveFile(ftpURL.replace("finishedSchematics/", ""), fileOptions); + remote.createFolder(); - // Create remote schematic and write to it - FileObject remoteSchematic = remote.resolveFile(schematic.getName()); - remoteSchematic.copyFrom(localSchematic, Selectors.SELECT_SELF); + // Create remote schematic and write to it + FileObject remoteSchematic = remote.resolveFile(schematic.getName()); + remoteSchematic.copyFrom(localSchematic, Selectors.SELECT_SELF); - localSchematic.close(); - remoteSchematic.close(); + localSchematic.close(); + remoteSchematic.close(); + } } - return CompletableFuture.completedFuture(null); } - public static CompletableFuture downloadSchematic(String ftpURL, File schematic) throws FileSystemException { + public static void downloadSchematic(String ftpURL, File schematic) throws FileSystemException { try (StandardFileSystemManager fileManager = new StandardFileSystemManager()) { fileManager.init(); @@ -81,6 +82,5 @@ public static CompletableFuture downloadSchematic(String ftpURL, File sche localSchematic.close(); remoteSchematic.close(); } - return CompletableFuture.completedFuture(null); } } diff --git a/src/main/java/com/alpsbte/plotsystemterra/utils/Utils.java b/src/main/java/com/alpsbte/plotsystemterra/utils/Utils.java index c388ab7..a35224f 100644 --- a/src/main/java/com/alpsbte/plotsystemterra/utils/Utils.java +++ b/src/main/java/com/alpsbte/plotsystemterra/utils/Utils.java @@ -35,4 +35,12 @@ public static String getErrorMessageFormat(String error) { public static boolean hasPermission(CommandSender sender, String permissionNode) { return sender.hasPermission(permissionPrefix + "." + permissionNode); } + + public static Integer tryParseInt(String integer) { + try { + return Integer.parseInt(integer); + } catch (NumberFormatException ex) { + return null; + } + } } diff --git a/src/main/resources/defaultConfig.yml b/src/main/resources/defaultConfig.yml index da33886..1d65826 100644 --- a/src/main/resources/defaultConfig.yml +++ b/src/main/resources/defaultConfig.yml @@ -24,6 +24,17 @@ database: password: minecraft +# ----------------------------------------------------- +# | Plot Scanning +# ----------------------------------------------------- + +# Additional to the scanned plot the environment around the plot is +# scanned to fill the area around the plot in the plot server +# [radius] -> in blocks around the plot; default: 50 blocks +environment: + enabled: true + radius: 50 + # ----------------------------------------------------- # | Plot Pasting # ----------------------------------------------------- @@ -56,4 +67,4 @@ error-colour: §c # NOTE: Do not change -config-version: 1.3 \ No newline at end of file +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 78f280f..f6cde15 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -1,12 +1,16 @@ -main: com.alpsbte.plotsystemterra.PlotSystemTerra -version: 2.1 -api-version: 1.12.2 -name: PlotSystem-Terra -author: R3tuxn, Coppertine -softdepend: [WorldEdit, HeadDatabase] - -commands: - createplot: - description: Creates a new plot for the PlotSystem. - usage: /createplot - permission: plotsystem.createplot \ No newline at end of file +main: com.alpsbte.plotsystemterra.PlotSystemTerra +version: 3.0 +api-version: 1.12.2 +name: PlotSystem-Terra +author: R3tuxn, Coppertine +softdepend: [WorldEdit, HeadDatabase] + +commands: + createplot: + description: Creates a new plot for the PlotSystem. + usage: /createplot + permission: plotsystem.createplot + pasteplot: + description: Pastes a plot manually with specific ID + usage: /pasteplot + permission: plotsystem.pasteplot \ No newline at end of file