diff --git a/.gitignore b/.gitignore index d15cf98..de52dbe 100644 --- a/.gitignore +++ b/.gitignore @@ -10,10 +10,15 @@ # Mobile Tools for Java (J2ME) .mtj.tmp/ -#Eclipse personal files +# Eclipse personal files .classpath .project +# Intellij files +*.iml +/.idea/ +/out/ + # Package Files # *.jar *.war diff --git a/config.yml b/config.yml deleted file mode 100644 index 880eae7..0000000 --- a/config.yml +++ /dev/null @@ -1,60 +0,0 @@ -configversion: 4 -#======================================== -# Custom Structures -#======================================== -#Wiki: https://github.com/ryandw11/CustomStructures/wiki - -#Log extra information in console when generating structures -debug: false - -#bstats is where data is sent. https://bstats.org/plugin/bukkit/CustomStructures . -#Here is some more information on it like what it collects and how to disable it all togeather and not just for this plugin. -# https://bstats.org/getting-started -bstats: true - -#Allow Plugins to delete structures at will. -allowDeletion: true - -Schematics: - #Change the name bellow with what you want. - demo: - #The schematic file. Make sure to include the .schem - Schematic: 'demo.schem' - #What biome this a schematic can spawn in. Change to all for all. - Biome: 'all' - #The chance option - Chance: - #The number that can be picked out of 1000. So if 1 is picked than it spawns. - Number: 1 - #The number that the ratio is out off. - OutOf: 1000 - #If the structure can spawn in all worlds. - AllWorlds: true - #If the above is false then the worlds it can spawn in: - AllowedWorlds: - - world - #The y cord the structure will spawn on. Use top if you want it to be on the surface. - SpawnY: top - #If you want air to be placed. True is air will be placed. False is air won't be placed. - PlaceAir: true - #Ignore stuff like grass and leaves. - ignorePlants: true - #Properties of the structure for the water. - Ocean_Properties: - #If you want the structure to spawn in water or lava. - spawnInLiquid: false - #If the structure picks a spot in the ocean or in a lake it will spawn at the floor of it. - #(SpawnY must be set to top) - useOceanFloor: true - #If the structure should be randomly rotated. - randomRotation: false - #Whitelisted blocks that the structure can spawn on. Remove this list if you do not want a whitelist. - whitelistSpawnBlocks: - - Grass_block - - Dirt - - Stone - - air - #List of lootTables for this Schematic, name and weight - #Weight determines how often it will be chosen out of all the entries in the list. - LootTables: - lootTable: 5 \ No newline at end of file diff --git a/src/com/ryandw11/structure/CustomStructures.java b/src/com/ryandw11/structure/CustomStructures.java index 6042223..99cac7a 100644 --- a/src/com/ryandw11/structure/CustomStructures.java +++ b/src/com/ryandw11/structure/CustomStructures.java @@ -1,165 +1,300 @@ -package com.ryandw11.structure; - -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileWriter; -import java.io.IOException; -import java.util.ArrayList; - -import org.bukkit.Bukkit; -import org.bukkit.configuration.InvalidConfigurationException; -import org.bukkit.configuration.file.FileConfiguration; -import org.bukkit.configuration.file.YamlConfiguration; -import org.bukkit.plugin.java.JavaPlugin; - -import com.ryandw11.structure.api.CustomStructuresAPI; -import com.ryandw11.structure.bstats.Metrics; -import com.ryandw11.structure.commands.SCommand; -import com.ryandw11.structure.listener.ChunkLoad; -import com.ryandw11.structure.listener.PlayerJoin; -import com.ryandw11.structure.loottables.LootTablesHandler; -import com.ryandw11.structure.mythicalmobs.MMDisabled; -import com.ryandw11.structure.mythicalmobs.MMEnabled; -import com.ryandw11.structure.mythicalmobs.MythicalMobHook; -import com.ryandw11.structure.utils.CheckLootTables; -import com.ryandw11.structure.utils.CheckSchematics; - -/** - * - * @author Ryandw11 - * @version 1.4.2 - * - */ - -public class CustomStructures extends JavaPlugin { - - public static CustomStructures plugin; - - public static LootTablesHandler lootTablesHandler; - - public File exfile = new File(getDataFolder() + "/schematics/Put_Schematics_In_Here.yml"); - public File lootTablesfile = new File(getDataFolder() + "/lootTables/lootTable.yml"); - public FileConfiguration ex = YamlConfiguration.loadConfiguration(exfile); - public FileConfiguration lootTablesFC = YamlConfiguration.loadConfiguration(lootTablesfile); - - public ArrayList structures; - public MythicalMobHook mmh; - - public static boolean enabled; - - @Override - public void onEnable() { - enabled = true; - - plugin = this; - loadManager(); - registerConfig(); - - if(getServer().getPluginManager().getPlugin("MythicMobs") != null) { - mmh = new MMEnabled(); - getLogger().info("MythicMobs detected! Activating plugin hook!"); - }else { - mmh = new MMDisabled(); - } - - CustomStructuresAPI capi = new CustomStructuresAPI(); - - getLogger().info("The plugin has been enabled with " + capi.getNumberOfStructures() + " structures."); - loadFile(); - CheckSchematics cs = new CheckSchematics(this.getConfig().getConfigurationSection("Schematics").getKeys(false)); - cs.runTaskTimer(plugin, 5L, 1L); - setStructures(); - - CheckLootTables cl = new CheckLootTables(this.getConfig().getConfigurationSection("Schematics").getKeys(false)); - cl.runTaskTimer(plugin, 5L, 1L); - - lootTablesHandler = new LootTablesHandler(); - - if(getConfig().getBoolean("bstats")){ - @SuppressWarnings("unused") - Metrics metrics = new Metrics(this, 7056); - getLogger().info("Bstat metrics for this plugin is enabled. Disable it in the config if you do not want it on."); - }else{ - getLogger().info("Bstat metrics is disabled for this plugin."); - } - } - - @Override - public void onDisable() { - saveFile(); - } - - public void loadManager() { - Bukkit.getServer().getPluginManager().registerEvents(new ChunkLoad(), this); - Bukkit.getServer().getPluginManager().registerEvents(new PlayerJoin(), this); - getCommand("customstructure").setExecutor(new SCommand()); - } - - public void setStructures() { - structures = new ArrayList(); - for (String s : this.getConfig().getConfigurationSection("Schematics").getKeys(false)) { - structures.add(s); - } - } - - public void registerConfig() { - saveDefaultConfig(); - } - - public void saveFile() { - try { - ex.save(exfile); - } catch (IOException e) { - e.printStackTrace(); - - } - } - - public void loadFile() { - if (exfile.exists()) { - try { - ex.load(exfile); - - } catch (IOException | InvalidConfigurationException e) { - - e.printStackTrace(); - } - } else { - try { - ex.save(exfile); - } catch (IOException e) { - e.printStackTrace(); - } - } - - if (lootTablesfile.exists()) { - try { - lootTablesFC.load(lootTablesfile); - - } catch (IOException | InvalidConfigurationException e) { - - e.printStackTrace(); - } - } else { - saveResource("lootTables/lootTable.yml", false); - } - } - - public void setupSchem() { - File fil = new File(plugin.getDataFolder() + "/schematics/Demo.txt"); - if (!fil.exists()) { - try (BufferedWriter bw = new BufferedWriter(new FileWriter("Demo.txt"))) { - - String content = "In this folder is where you put schematics. For help go here: {Insert Github Link}"; - - bw.write(content); - - } catch (IOException e1) { - - e1.printStackTrace(); - - } - } - } - -} +package com.ryandw11.structure; + +import java.io.File; +import java.io.IOException; +import java.util.*; + +import com.ryandw11.structure.commands.SCommandTab; +import com.ryandw11.structure.loottables.customitems.CustomItemManager; +import com.ryandw11.structure.structure.StructureBuilder; +import com.ryandw11.structure.structure.StructureHandler; +import com.ryandw11.structure.structure.properties.*; +import com.ryandw11.structure.utils.RandomCollection; +import org.bukkit.Bukkit; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.InvalidConfigurationException; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.craftbukkit.libs.org.apache.commons.io.FileUtils; +import org.bukkit.plugin.java.JavaPlugin; + +import com.ryandw11.structure.bstats.Metrics; +import com.ryandw11.structure.commands.SCommand; +import com.ryandw11.structure.listener.ChunkLoad; +import com.ryandw11.structure.listener.PlayerJoin; +import com.ryandw11.structure.loottables.LootTablesHandler; +import com.ryandw11.structure.mythicalmobs.MMDisabled; +import com.ryandw11.structure.mythicalmobs.MMEnabled; +import com.ryandw11.structure.mythicalmobs.MythicalMobHook; + +/** + * + * @author Ryandw11 + * @version 1.5 + * + */ + +public class CustomStructures extends JavaPlugin { + + public static CustomStructures plugin; + + public File exfile = new File(getDataFolder() + "/schematics/Put_Schematics_In_Here.yml"); + public File lootTablesfile = new File(getDataFolder() + "/lootTables/lootTable.yml"); + public FileConfiguration ex = YamlConfiguration.loadConfiguration(exfile); + public FileConfiguration lootTablesFC = YamlConfiguration.loadConfiguration(lootTablesfile); + + public MythicalMobHook mmh; + + private StructureHandler structureHandler; + private LootTablesHandler lootTablesHandler; + private CustomItemManager customItemManager; + private boolean debugMode; + + public static boolean enabled; + + @Override + public void onEnable() { + enabled = true; + + plugin = this; + loadManager(); + registerConfig(); + + if(getServer().getPluginManager().getPlugin("MythicMobs") != null) { + mmh = new MMEnabled(); + getLogger().info("MythicMobs detected! Activating plugin hook!"); + }else + mmh = new MMDisabled(); + loadFile(); + debugMode = getConfig().getBoolean("debug"); + if(getConfig().getInt("configversion") < 5){ + lootTablesHandler = new LootTablesHandler(); + updateConfig(); + } + + File f = new File(getDataFolder() + File.separator + "structures"); + if(!f.exists()){ + saveResource("structures/demo.yml", false); + } + f = new File(getDataFolder() + File.separator + "schematics"); + if(!f.exists()){ + saveResource("schematics/demo.schem", false); + getLogger().info("Loading the plugin for the first time."); + getLogger().info("A demo structure was added! Please make sure to test out this plugin in a test world!"); + } + + this.customItemManager = new CustomItemManager(this, new File(getDataFolder() + File.separator + "items" + File.separator + "customitems.yml"), new File(getDataFolder() + File.separator + "items")); + + lootTablesHandler = new LootTablesHandler(); + this.structureHandler = new StructureHandler(getConfig().getStringList("Structures"), this); + + getLogger().info("The plugin has been enabled with " + structureHandler.getStructures().size() + " structures."); + + if(getConfig().getBoolean("bstats")){ + new Metrics(this, 7056); + getLogger().info("Bstat metrics for this plugin is enabled. Disable it in the config if you do not want it on."); + }else{ + getLogger().info("Bstat metrics is disabled for this plugin."); + } + } + + @Override + public void onDisable() { + saveFile(); + } + + /** + * Get the structure handler for the plugin. + * @return The structure handler. + */ + public StructureHandler getStructureHandler(){ + return structureHandler; + } + + /** + * Get the loot table handler. + * @return The loot table handler. + */ + public LootTablesHandler getLootTableHandler(){ + return lootTablesHandler; + } + + /** + * Reload the handlers. + *

This is for internal use only.

+ */ + public void reloadHandlers(){ + this.structureHandler = new StructureHandler(getConfig().getStringList("Structures"), this); + this.lootTablesHandler = new LootTablesHandler(); + } + + /** + * Get the instance of the main class. + * @return The main class. + */ + public static CustomStructures getInstance(){ + return plugin; + } + + private void loadManager() { + Bukkit.getServer().getPluginManager().registerEvents(new ChunkLoad(), this); + Bukkit.getServer().getPluginManager().registerEvents(new PlayerJoin(), this); + getCommand("customstructure").setExecutor(new SCommand(this)); + getCommand("customstructure").setTabCompleter(new SCommandTab()); + } + + private void registerConfig() { + saveDefaultConfig(); + } + + public void saveFile() { + try { + ex.save(exfile); + } catch (IOException e) { + e.printStackTrace(); + + } + } + + public void loadFile() { + if (exfile.exists()) { + try { + ex.load(exfile); + + } catch (IOException | InvalidConfigurationException e) { + + e.printStackTrace(); + } + } else { + try { + ex.save(exfile); + } catch (IOException e) { + e.printStackTrace(); + } + } + + if (lootTablesfile.exists()) { + try { + lootTablesFC.load(lootTablesfile); + + } catch (IOException | InvalidConfigurationException e) { + + e.printStackTrace(); + } + } else { + saveResource("lootTables/lootTable.yml", false); + } + } + + /** + * Updates the config to the latest version. + * (Config version 4 to version 5) + * This will be removed in future versions. + */ + private void updateConfig(){ + getLogger().info("An older version of the plugin has been detected!"); + getLogger().info("Automatically converting old format into the new one."); + + List structures = new ArrayList<>(); + File config = new File(getDataFolder(), "config.yml"); + File configBackup = new File(getDataFolder(), "config.yml.backup"); + File structuresDir = new File(getDataFolder(), "structures"); + try{ + configBackup.createNewFile(); + FileUtils.copyFile(config, configBackup); + if(!structuresDir.exists()) + structuresDir.mkdir(); + }catch(IOException ex){ + getLogger().severe("The config converter failed to create a backup of the config!"); + getLogger().severe("Custom Structures will now disable itself."); + getServer().getPluginManager().disablePlugin(this); + if(debugMode) + ex.printStackTrace(); + return; + } + for(String key : Objects.requireNonNull(getConfig().getConfigurationSection("Schematics")).getKeys(false)){ + ConfigurationSection section = getConfig().getConfigurationSection("Schematics." + key); + File file = new File(getDataFolder() + File.separator + "structures" + File.separator + key + ".yml"); + assert section != null; + StructureBuilder builder = new StructureBuilder(key, section.getString("Schematic")); + + builder.setChance(section.getInt("Chance.Number"), section.getInt("Chance.OutOf")); + + if(section.contains("whitelistSpawnBlocks")) + builder.setStructureLimitations(new StructureLimitations(section.getStringList("whitelistSpawnBlocks"), new BlockLevelLimit(), new HashMap<>())); + else + builder.setStructureLimitations(new StructureLimitations(new ArrayList<>(), new BlockLevelLimit(), new HashMap<>())); + + StructureLocation structLocation = new StructureLocation(); + if(section.contains("AllowedWorlds") && section.contains("AllWorlds")){ + if(!section.getBoolean("AllWorlds")){ + structLocation.setWorlds(section.getStringList("AllowedWorlds")); + } + } + if(section.contains("SpawnY")){ + structLocation.setSpawnSettings(new StructureYSpawning(Objects.requireNonNull(section.getString("SpawnY")))); + } + if(section.contains("Biome")){ + String biomeValue = section.getString("Biome"); + assert biomeValue != null; + if(!biomeValue.equalsIgnoreCase("all")){ + String[] biomes = biomeValue.split(","); + structLocation.setBiomes(new ArrayList<>(Arrays.asList(biomes))); + } + } + builder.setStructureLocation(structLocation); + + StructureProperties structProperties = new StructureProperties(); + structProperties.setIgnorePlants(section.getBoolean("ignorePlants")); + structProperties.setPlaceAir(section.getBoolean("PlaceAir")); + structProperties.setRandomRotation(section.getBoolean("randomRotation")); + structProperties.setSpawnInWater(section.getBoolean("Ocean_Properties.spawnInLiquid")); + builder.setStructureProperties(structProperties); + + if(section.contains("LootTables")) + builder.setLootTables(Objects.requireNonNull(section.getConfigurationSection("LootTables"))); + else + builder.setLootTables(new RandomCollection<>()); + + try{ + builder.save(file); + }catch(IOException ex){ + getLogger().severe("The config converter failed to convert a structure to the new format!"); + getLogger().severe("The structure that failed is " + key); + getLogger().severe("Custom Structures will now disable itself."); + getServer().getPluginManager().disablePlugin(this); + if(debugMode) + ex.printStackTrace(); + return; + } + structures.add(key); + } + getConfig().set("Schematics", null); + getConfig().set("Structures", structures); + getConfig().set("configversion", 5); + try{ + getConfig().save(plugin.getDataFolder() + File.separator + "config.yml"); + }catch(IOException ex){ + getLogger().severe("An error has occurred when trying to save the new config."); + getLogger().severe("The plugin will now disable itself."); + getServer().getPluginManager().disablePlugin(this); + if(debugMode) + ex.printStackTrace(); + return; + } + reloadConfig(); + getLogger().info("Successfully converted " + structures.size() + " structures to the new format!"); + } + + public boolean isDebug(){ + return debugMode; + } + + /** + * Get the custom item manager. + * @return The custom item manager. + */ + public CustomItemManager getCustomItemManager() { + return customItemManager; + } +} diff --git a/src/com/ryandw11/structure/SchematicHandeler.java b/src/com/ryandw11/structure/SchematicHandeler.java index 79033a1..a7bc4ea 100644 --- a/src/com/ryandw11/structure/SchematicHandeler.java +++ b/src/com/ryandw11/structure/SchematicHandeler.java @@ -1,347 +1,401 @@ -package com.ryandw11.structure; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.Random; - -import com.ryandw11.structure.utils.GetBlocksInArea; -import org.bukkit.Bukkit; -import org.bukkit.ChatColor; -import org.bukkit.Location; -import org.bukkit.Material; -import org.bukkit.block.BlockState; -import org.bukkit.block.Chest; -import org.bukkit.block.Container; -import org.bukkit.block.DoubleChest; -import org.bukkit.block.Sign; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.entity.EntityType; -import org.bukkit.inventory.BrewerInventory; -import org.bukkit.inventory.FurnaceInventory; -import org.bukkit.inventory.Inventory; -import org.bukkit.inventory.InventoryHolder; -import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.meta.ItemMeta; - -import com.ryandw11.structure.loottables.LootTable; -import com.ryandw11.structure.loottables.LootTablesHandler; -import com.ryandw11.structure.utils.RandomCollection; -import com.sk89q.worldedit.EditSession; -import com.sk89q.worldedit.WorldEdit; -import com.sk89q.worldedit.WorldEditException; -import com.sk89q.worldedit.bukkit.BukkitAdapter; -import com.sk89q.worldedit.extent.clipboard.Clipboard; -import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormat; -import com.sk89q.worldedit.extent.clipboard.io.ClipboardFormats; -import com.sk89q.worldedit.extent.clipboard.io.ClipboardReader; -import com.sk89q.worldedit.function.operation.Operation; -import com.sk89q.worldedit.function.operation.Operations; -import com.sk89q.worldedit.math.BlockVector3; -import com.sk89q.worldedit.math.transform.AffineTransform; -import com.sk89q.worldedit.session.ClipboardHolder; - -public class SchematicHandeler { - - private CustomStructures plugin; - private LootTablesHandler lootTablesHandler; - - public SchematicHandeler() { - this.plugin = CustomStructures.plugin; - this.lootTablesHandler = CustomStructures.lootTablesHandler; - } - - /** - * Handles the schematic. - * - * @param loc - The location - * @param filename - The file name. Ex: demo.schematic - * @param useAir - if air is to be used in the schematic - * @param lootTables - The Loot Tables specified for this structure, if any. - * @param cs - The configurationsection for this structure. - * @throws WorldEditException - */ - public void schemHandle(Location loc, String filename, boolean useAir, RandomCollection lootTables, ConfigurationSection cs) - throws IOException, WorldEditException { - File schematicFile = new File(plugin.getDataFolder() + "/schematics/" + filename); - // Check to see if the schematic is a thing. - if (!schematicFile.exists()) { - Bukkit.broadcastMessage(ChatColor.translateAlternateColorCodes('&', - "&3[&2CustomStructures&3] &cA fatal error has occurred! Please check the console for errors.")); - plugin.getLogger().warning("Error: The schematic " + filename + " does not exist!"); - plugin.getLogger().warning( - "If this is your first time using this plugin you need to put a schematic in the schematic folder."); - plugin.getLogger().warning("Then add it into the config."); - plugin.getLogger().warning( - "If you need help look at the wiki: https://github.com/ryandw11/CustomStructures/wiki or contact Ryandw11 on spigot!"); - plugin.getLogger().warning("The plugin will now disable to prevent damage to the server."); - Bukkit.getPluginManager().disablePlugin(plugin); - return; - } - ClipboardFormat format = ClipboardFormats.findByFile(schematicFile); - Clipboard clipboard; - - try (ClipboardReader reader = format.getReader(new FileInputStream(schematicFile))) { - clipboard = reader.read(); - } - - ClipboardHolder ch = new ClipboardHolder(clipboard); - AffineTransform transform = new AffineTransform(); - int rotY = 0; - - // If random rotation is enabled, rotate the clipboard - if(cs.contains("randomRotation") && cs.getBoolean("randomRotation")) { - rotY = new Random().nextInt(4)*90; - transform = transform.rotateY(rotY); - ch.setTransform(ch.getTransform().combine(transform)); - } - - // Paste the schematic - try (EditSession editSession = WorldEdit.getInstance().getEditSessionFactory() - .getEditSession(BukkitAdapter.adapt(loc.getWorld()), -1)) { - Operation operation = ch.createPaste(editSession) - .to(BlockVector3.at(loc.getX(), loc.getY(), loc.getZ())).ignoreAirBlocks(!useAir).build(); - Operations.complete(operation); - - if(plugin.getConfig().getBoolean("debug")){ - plugin.getLogger().info(String.format("(%s) Created an instance of %s at %s, %s, %s with rotation %s", loc.getWorld().getName(), filename, loc.getBlockX(), loc.getBlockY(), loc.getBlockZ(), rotY)); - } - } - - //Schedule the signs & containers replacement task - int finalRotY = rotY; - this.plugin.getServer().getScheduler().scheduleSyncDelayedTask(this.plugin, () -> { - List containersAndSignsLocations = getContainersAndSignsLocations(ch.getClipboard(), loc, finalRotY); - for (Location location : containersAndSignsLocations) { - if (location.getBlock().getState() instanceof Container) { - replaceContainerContent(lootTables, location); - } else if (location.getBlock().getState() instanceof Sign) { - replaceSignWithMob(location); - } - } - }); - - } - - /** - * Get the location of containers and signs. - * @param clipboard The worldedit clipboard - * @param pasteLocation The location of the paste - * @param rotation The rotate value (in degrees). - * @return The list of locations - */ - private List getContainersAndSignsLocations(Clipboard clipboard, Location pasteLocation, int rotation) { - - BlockVector3 originalOrigin = clipboard.getOrigin(); - BlockVector3 originalMinimumPoint = clipboard.getRegion().getMinimumPoint(); - BlockVector3 originalMaximumPoint = clipboard.getRegion().getMaximumPoint(); - - BlockVector3 originalMinimumOffset = originalOrigin.subtract(originalMinimumPoint); - BlockVector3 originalMaximumOffset = originalOrigin.subtract(originalMaximumPoint); - - BlockVector3 newOrigin = BukkitAdapter.asBlockVector(pasteLocation); - BlockVector3 newMinimumPoint = newOrigin.subtract(originalMinimumOffset); - BlockVector3 newMaximumPoint = newOrigin.subtract(originalMaximumOffset); - - BlockVector3 newRotatedMinimumPoint = rotateAround(newMinimumPoint, newOrigin, rotation); - BlockVector3 newRotatedMaximumPoint = rotateAround(newMaximumPoint, newOrigin, rotation); - - Location minLoc = new Location(pasteLocation.getWorld(), newRotatedMinimumPoint.getX(), newRotatedMinimumPoint.getY(), newRotatedMinimumPoint.getZ()); - Location maxLoc = new Location(pasteLocation.getWorld(), newRotatedMaximumPoint.getX(), newRotatedMaximumPoint.getY(), newRotatedMaximumPoint.getZ()); - - List locations = new ArrayList<>(); - for (Location location : GetBlocksInArea.getLocationListBetween(minLoc, maxLoc)) { - - BlockState blockState = location.getBlock().getState(); - - if (blockState instanceof Container) { - if (blockState instanceof Chest) { - InventoryHolder holder = ((Chest) blockState).getInventory().getHolder(); - if (holder instanceof DoubleChest) { - DoubleChest doubleChest = ((DoubleChest) holder); - Location leftSideLocation = ((Chest) doubleChest.getLeftSide()).getLocation(); - Location rightSideLocation = ((Chest) doubleChest.getRightSide()).getLocation(); - - Location roundedLocation = new Location(location.getWorld(), - Math.floor(location.getX()), Math.floor(location.getY()), - Math.floor(location.getZ())); - - // Check to see if this (or the other) side of the chest is already in the list - if (leftSideLocation.distance(roundedLocation) < 1) { - if (this.isNotAlreadyIn(locations, rightSideLocation)) { - locations.add(roundedLocation); - } - - } else if (rightSideLocation.distance(roundedLocation) < 1) { - if (this.isNotAlreadyIn(locations, leftSideLocation)) { - locations.add(roundedLocation); - } - } - - } else if (holder instanceof Chest) { - locations.add(location); - } - } else { - locations.add(location); - } - } else if (blockState instanceof Sign) { - locations.add(location); - } - }// - return locations; - } - - /** - * Checks to see if a location is not already inside of a list of locations. - * @param locations - * @param location - * @return - */ - private boolean isNotAlreadyIn(List locations, Location location) { - for (Location auxLocation : locations) { - if (location.distance(auxLocation) < 1) { - return false; - } - } - return true; - } - - /** - * Replace the contents of a container with the loottable. - * @param lootTables - * @param location - */ - private void replaceContainerContent(RandomCollection lootTables, Location location) { - String lootTableName = lootTables.next(); - Random random = new Random(); - LootTable lootTable = this.lootTablesHandler.getLootTableByName(lootTableName); - - for (int i = 0; i < lootTable.getRolls(); i++) { - BlockState blockState = location.getBlock().getState(); - Container container = (Container) blockState; - Inventory containerInventory = container.getInventory(); - if (containerInventory instanceof FurnaceInventory) { - this.replaceFurnaceContent(lootTable, random, (FurnaceInventory) containerInventory); - } else if (containerInventory instanceof BrewerInventory) { - this.replaceBrewerContent(lootTable, random, (BrewerInventory) containerInventory); - } else { - this.replaceChestContent(lootTable, random, containerInventory); - } - } - - } - - /** - * Spawn a mob with the signs. - * @param location - */ - private void replaceSignWithMob(Location location) { - Sign sign = (Sign) location.getBlock().getState(); - String firstLine = sign.getLine(0).trim(); - String secondLine = sign.getLine(1).trim(); - - if (firstLine.equalsIgnoreCase("[mob]")) { - try { - location.getWorld().spawnEntity(location, EntityType.valueOf(secondLine.toUpperCase())); - location.getBlock().setType(Material.AIR); - } catch (IllegalArgumentException e) { - } - } - if (firstLine.equalsIgnoreCase("[mythicmob]") || firstLine.equalsIgnoreCase("[mythicalmob]")) { - plugin.mmh.spawnMob(secondLine, location); - location.getBlock().setType(Material.AIR); - } - - } - - private void replaceChestContent(LootTable lootTable, Random random, Inventory containerInventory) { - ItemStack[] containerContent = containerInventory.getContents(); - - ItemStack randomItem = lootTable.getRandomWeightedItem(); - - for (int j = 0; j < randomItem.getAmount(); j++) { - boolean done = false; - int attemps = 0; - while (!done) { - int randomPos = random.nextInt(containerContent.length); - ItemStack randomPosItem = containerInventory.getItem(randomPos); - if (randomPosItem != null) { - - if (this.isSameItem(randomPosItem, randomItem)) { - if (randomPosItem.getAmount() < randomItem.getMaxStackSize()) { - ItemStack randomItemCopy = randomItem.clone(); - int newAmount = randomPosItem.getAmount() + 1; - randomItemCopy.setAmount(newAmount); - containerContent[randomPos] = randomItemCopy; - containerInventory.setContents(containerContent); - done = true; - } - } - } else { - ItemStack randomItemCopy = randomItem.clone(); - randomItemCopy.setAmount(1); - containerContent[randomPos] = randomItemCopy; - containerInventory.setContents(containerContent); - done = true; - - } - attemps++; - if (attemps >= containerContent.length) { - done = true; - } - } - } - } - - private boolean isSameItem(ItemStack randomPosItem, ItemStack randomItem) { - ItemMeta randomPosItemMeta = randomPosItem.getItemMeta(); - ItemMeta randomItemMeta = randomItem.getItemMeta(); - - return randomPosItem.getType().equals(randomItem.getType()) && randomPosItemMeta.equals(randomItemMeta); - } - - private void replaceBrewerContent(LootTable lootTable, Random random, BrewerInventory containerInventory) { - ItemStack item = lootTable.getRandomWeightedItem(); - ItemStack ingredient = containerInventory.getIngredient(); - ItemStack fuel = containerInventory.getFuel(); - - if ((ingredient == null) || ingredient.equals(item)) { - containerInventory.setIngredient(item); - } else if ((fuel == null) || fuel.equals(item)) { - containerInventory.setFuel(item); - } - - } - - private void replaceFurnaceContent(LootTable lootTable, Random random, FurnaceInventory containerInventory) { - ItemStack item = lootTable.getRandomWeightedItem(); - ItemStack result = containerInventory.getResult(); - ItemStack fuel = containerInventory.getFuel(); - ItemStack smelting = containerInventory.getSmelting(); - - if ((result == null) || result.equals(item)) { - containerInventory.setResult(item); - } else if ((fuel == null) || fuel.equals(item)) { - containerInventory.setFuel(item); - } else if ((smelting == null) || smelting.equals(item)) { - containerInventory.setSmelting(item); - } - } - - /** - * Rotate the point around the center. - * @param point The point - * @param center The center - * @param angle The angle to rotate by. - * @return The final position. - */ - private BlockVector3 rotateAround(BlockVector3 point, BlockVector3 center, double angle){ - angle = Math.toRadians(angle * -1); - double rotatedX = Math.cos(angle) * (point.getX() - center.getX()) - Math.sin(angle) * (point.getZ() - center.getZ()) + center.getX(); - double rotatedZ = Math.sin(angle) * (point.getX() - center.getX()) + Math.cos(angle) * (point.getZ() - center.getZ()) + center.getZ(); - - return BlockVector3.at(rotatedX, point.getY(), rotatedZ); - } +package com.ryandw11.structure; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.Random; + +import com.ryandw11.structure.structure.Structure; +import com.ryandw11.structure.utils.GetBlocksInArea; +import com.sk89q.worldedit.IncompleteRegionException; +import com.sk89q.worldedit.bukkit.WorldEditPlugin; +import com.sk89q.worldedit.extent.clipboard.BlockArrayClipboard; +import com.sk89q.worldedit.extent.clipboard.io.*; +import com.sk89q.worldedit.function.operation.ForwardExtentCopy; +import com.sk89q.worldedit.internal.annotation.Selection; +import com.sk89q.worldedit.regions.CuboidRegion; +import com.sk89q.worldedit.regions.Region; +import org.bukkit.*; +import org.bukkit.block.*; +import org.bukkit.entity.Entity; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Player; +import org.bukkit.inventory.BrewerInventory; +import org.bukkit.inventory.FurnaceInventory; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; + +import com.ryandw11.structure.loottables.LootTable; +import com.ryandw11.structure.utils.RandomCollection; +import com.sk89q.worldedit.EditSession; +import com.sk89q.worldedit.WorldEdit; +import com.sk89q.worldedit.WorldEditException; +import com.sk89q.worldedit.bukkit.BukkitAdapter; +import com.sk89q.worldedit.extent.clipboard.Clipboard; +import com.sk89q.worldedit.function.operation.Operation; +import com.sk89q.worldedit.function.operation.Operations; +import com.sk89q.worldedit.math.BlockVector3; +import com.sk89q.worldedit.math.transform.AffineTransform; +import com.sk89q.worldedit.session.ClipboardHolder; + +public class SchematicHandeler { + + private CustomStructures plugin; + + public SchematicHandeler() { + this.plugin = CustomStructures.plugin; + } + + /** + * Handles the schematic. + *

This method is to be called on the main Server thread.

+ * @param loc - The location + * @param filename - The file name. Ex: demo.schematic + * @param useAir - if air is to be used in the schematic + * @param lootTables - The Loot Tables specified for this structure, if any. + * @param structure - The structure that is getting spawned. + * @throws WorldEditException If world edit has a problem pasting the schematic. + */ + public void schemHandle(Location loc, String filename, boolean useAir, RandomCollection lootTables, Structure structure) + throws IOException, WorldEditException { + File schematicFile = new File(plugin.getDataFolder() + "/schematics/" + filename); + // Check to see if the schematic is a thing. + if (!schematicFile.exists()) { + Bukkit.broadcastMessage(ChatColor.translateAlternateColorCodes('&', + "&3[&2CustomStructures&3] &cA fatal error has occurred! Please check the console for errors.")); + plugin.getLogger().warning("Error: The schematic " + filename + " does not exist!"); + plugin.getLogger().warning( + "If this is your first time using this plugin you need to put a schematic in the schematic folder."); + plugin.getLogger().warning("Then add it into the config."); + plugin.getLogger().warning( + "If you need help look at the wiki: https://github.com/ryandw11/CustomStructures/wiki or contact Ryandw11 on spigot!"); + plugin.getLogger().warning("The plugin will now disable to prevent damage to the server."); + Bukkit.getPluginManager().disablePlugin(plugin); + return; + } + ClipboardFormat format = ClipboardFormats.findByFile(schematicFile); + Clipboard clipboard; + + try (ClipboardReader reader = format.getReader(new FileInputStream(schematicFile))) { + clipboard = reader.read(); + } + + ClipboardHolder ch = new ClipboardHolder(clipboard); + AffineTransform transform = new AffineTransform(); + int rotY = 0; + + // If random rotation is enabled, rotate the clipboard + if (structure.getStructureProperties().isRandomRotation()) { + rotY = new Random().nextInt(4) * 90; + transform = transform.rotateY(rotY); + ch.setTransform(ch.getTransform().combine(transform)); + } + + // Paste the schematic + try (EditSession editSession = WorldEdit.getInstance().getEditSessionFactory() + .getEditSession(BukkitAdapter.adapt(loc.getWorld()), -1)) { + Operation operation = ch.createPaste(editSession) + .to(BlockVector3.at(loc.getX(), loc.getY(), loc.getZ())).ignoreAirBlocks(!useAir).build(); + Operations.complete(operation); + + if (plugin.getConfig().getBoolean("debug")) { + plugin.getLogger().info(String.format("(%s) Created an instance of %s at %s, %s, %s with rotation %s", loc.getWorld().getName(), filename, loc.getBlockX(), loc.getBlockY(), loc.getBlockZ(), rotY)); + } + } + + //Schedule the signs & containers replacement task + int finalRotY = rotY; + List containersAndSignsLocations = getContainersAndSignsLocations(ch.getClipboard(), loc, finalRotY, structure); + for (Location location : containersAndSignsLocations) { + if (location.getBlock().getState() instanceof Container) { + replaceContainerContent(lootTables, location); + } else if (location.getBlock().getState() instanceof Sign) { + replaceSignWithMob(location); + } + } + } + + /** + * Create a schematic and save it to the schematics folder in the CustomStructures plugin. + * @param name The name of the schematic. + * @param player The player. + * @param w The world + * @return If the operation was successful. + */ + public boolean createSchematic(String name, Player player, World w) { + try{ + WorldEditPlugin worldEditPlugin = (WorldEditPlugin) Bukkit.getServer().getPluginManager().getPlugin("WorldEdit"); + assert worldEditPlugin != null; + Region selection = worldEditPlugin.getSession(player).getSelection(BukkitAdapter.adapt(w)); + CuboidRegion region = new CuboidRegion(selection.getWorld(), selection.getMinimumPoint(), selection.getMaximumPoint()); + BlockArrayClipboard clipboard = new BlockArrayClipboard(region); + + try (EditSession editSession = WorldEdit.getInstance().getEditSessionFactory().getEditSession(selection.getWorld(), -1)) { + ForwardExtentCopy forwardExtentCopy = new ForwardExtentCopy( + editSession, region, clipboard, region.getMinimumPoint() + ); + // configure here + Operations.complete(forwardExtentCopy); + } catch (WorldEditException e) { + e.printStackTrace(); + } + + File file = new File(plugin.getDataFolder() + File.separator + "schematics" + File.separator + name + ".schem"); + + try (ClipboardWriter writer = BuiltInClipboardFormat.SPONGE_SCHEMATIC.getWriter(new FileOutputStream(file))) { + writer.write(clipboard); + } catch (IOException e) { + e.printStackTrace(); + } + return true; + } catch (IncompleteRegionException ex){ + return false; + } + } + + /** + * Get the location of containers and signs. + * + * @param clipboard The worldedit clipboard + * @param pasteLocation The location of the paste + * @param rotation The rotate value (in degrees). + * @return The list of locations + */ + private List getContainersAndSignsLocations(Clipboard clipboard, Location pasteLocation, int rotation, Structure structure) { + + BlockVector3 originalOrigin = clipboard.getOrigin(); + BlockVector3 originalMinimumPoint = clipboard.getRegion().getMinimumPoint(); + BlockVector3 originalMaximumPoint = clipboard.getRegion().getMaximumPoint(); + + BlockVector3 originalMinimumOffset = originalOrigin.subtract(originalMinimumPoint); + BlockVector3 originalMaximumOffset = originalOrigin.subtract(originalMaximumPoint); + + BlockVector3 newOrigin = BukkitAdapter.asBlockVector(pasteLocation); + BlockVector3 newMinimumPoint = newOrigin.subtract(originalMinimumOffset); + BlockVector3 newMaximumPoint = newOrigin.subtract(originalMaximumOffset); + + BlockVector3 newRotatedMinimumPoint = rotateAround(newMinimumPoint, newOrigin, rotation); + BlockVector3 newRotatedMaximumPoint = rotateAround(newMaximumPoint, newOrigin, rotation); + + Location minLoc = new Location(pasteLocation.getWorld(), newRotatedMinimumPoint.getX(), newRotatedMinimumPoint.getY(), newRotatedMinimumPoint.getZ()); + Location maxLoc = new Location(pasteLocation.getWorld(), newRotatedMaximumPoint.getX(), newRotatedMaximumPoint.getY(), newRotatedMaximumPoint.getZ()); + + List locations = new ArrayList<>(); + for (Location location : GetBlocksInArea.getLocationListBetween(minLoc, maxLoc)) { + + Block block = location.getBlock(); + BlockState blockState = location.getBlock().getState(); + + if (blockState instanceof Container) { + if (blockState instanceof Chest) { + InventoryHolder holder = ((Chest) blockState).getInventory().getHolder(); + if (holder instanceof DoubleChest) { + DoubleChest doubleChest = ((DoubleChest) holder); + Location leftSideLocation = ((Chest) doubleChest.getLeftSide()).getLocation(); + Location rightSideLocation = ((Chest) doubleChest.getRightSide()).getLocation(); + + Location roundedLocation = new Location(location.getWorld(), + Math.floor(location.getX()), Math.floor(location.getY()), + Math.floor(location.getZ())); + + // Check to see if this (or the other) side of the chest is already in the list + if (leftSideLocation.distance(roundedLocation) < 1) { + if (this.isNotAlreadyIn(locations, rightSideLocation)) { + locations.add(roundedLocation); + } + + } else if (rightSideLocation.distance(roundedLocation) < 1) { + if (this.isNotAlreadyIn(locations, leftSideLocation)) { + locations.add(roundedLocation); + } + } + + } else if (holder instanceof Chest) { + locations.add(location); + } + } else { + locations.add(location); + } + } else if (blockState instanceof Sign) { + locations.add(location); + }else{ + // For the block replacement system. + if(!structure.getStructureLimitations().getBlockReplacement().isEmpty()){ + if(structure.getStructureLimitations().getBlockReplacement().containsKey(block.getType())){ + block.setType(structure.getStructureLimitations().getBlockReplacement().get(block.getType())); + block.getState().update(); + } + } + } + }// + return locations; + } + + /** + * Checks to see if a location is not already inside of a list of locations. + * + * @param locations The list of locations. + * @param location The location to check + * @return If it is not already in. + */ + private boolean isNotAlreadyIn(List locations, Location location) { + for (Location auxLocation : locations) { + if (location.distance(auxLocation) < 1) { + return false; + } + } + return true; + } + + /** + * Replace the contents of a container with the loottable. + * + * @param lootTables The loot table + * @param location The location of the container. + */ + private void replaceContainerContent(RandomCollection lootTables, Location location) { + if(lootTables.isEmpty()) return; + LootTable lootTable = lootTables.next(); + Random random = new Random(); + + for (int i = 0; i < lootTable.getRolls(); i++) { + BlockState blockState = location.getBlock().getState(); + Container container = (Container) blockState; + Inventory containerInventory = container.getInventory(); + if (containerInventory instanceof FurnaceInventory) { + this.replaceFurnaceContent(lootTable, random, (FurnaceInventory) containerInventory); + } else if (containerInventory instanceof BrewerInventory) { + this.replaceBrewerContent(lootTable, random, (BrewerInventory) containerInventory); + } else { + this.replaceChestContent(lootTable, random, containerInventory); + } + } + + } + + /** + * Spawn a mob with the signs. + * + * @param location The location of the sign. + */ + private void replaceSignWithMob(Location location) { + Sign sign = (Sign) location.getBlock().getState(); + String firstLine = sign.getLine(0).trim(); + String secondLine = sign.getLine(1).trim(); + + if (firstLine.equalsIgnoreCase("[mob]")) { + try { + Entity ent = Objects.requireNonNull(location.getWorld()).spawnEntity(location, EntityType.valueOf(secondLine.toUpperCase())); + if(ent instanceof LivingEntity){ + LivingEntity livingEntity = (LivingEntity) ent; + livingEntity.setRemoveWhenFarAway(false); + } + location.getBlock().setType(Material.AIR); + } catch (IllegalArgumentException e) { + plugin.getLogger().warning("Invalid mob type on structure sign."); + } + } + if (firstLine.equalsIgnoreCase("[mythicmob]") || firstLine.equalsIgnoreCase("[mythicalmob]")) { + plugin.mmh.spawnMob(secondLine, location); + location.getBlock().setType(Material.AIR); + } + + } + + private void replaceChestContent(LootTable lootTable, Random random, Inventory containerInventory) { + ItemStack[] containerContent = containerInventory.getContents(); + + ItemStack randomItem = lootTable.getRandomWeightedItem(); + + for (int j = 0; j < randomItem.getAmount(); j++) { + boolean done = false; + int attemps = 0; + while (!done) { + int randomPos = random.nextInt(containerContent.length); + ItemStack randomPosItem = containerInventory.getItem(randomPos); + if (randomPosItem != null) { + + if (this.isSameItem(randomPosItem, randomItem)) { + if (randomPosItem.getAmount() < randomItem.getMaxStackSize()) { + ItemStack randomItemCopy = randomItem.clone(); + int newAmount = randomPosItem.getAmount() + 1; + randomItemCopy.setAmount(newAmount); + containerContent[randomPos] = randomItemCopy; + containerInventory.setContents(containerContent); + done = true; + } + } + } else { + ItemStack randomItemCopy = randomItem.clone(); + randomItemCopy.setAmount(1); + containerContent[randomPos] = randomItemCopy; + containerInventory.setContents(containerContent); + done = true; + + } + attemps++; + if (attemps >= containerContent.length) { + done = true; + } + } + } + } + + private boolean isSameItem(ItemStack randomPosItem, ItemStack randomItem) { + ItemMeta randomPosItemMeta = randomPosItem.getItemMeta(); + ItemMeta randomItemMeta = randomItem.getItemMeta(); + + return randomPosItem.getType().equals(randomItem.getType()) && randomPosItemMeta.equals(randomItemMeta); + } + + private void replaceBrewerContent(LootTable lootTable, Random random, BrewerInventory containerInventory) { + ItemStack item = lootTable.getRandomWeightedItem(); + ItemStack ingredient = containerInventory.getIngredient(); + ItemStack fuel = containerInventory.getFuel(); + + if ((ingredient == null) || ingredient.equals(item)) { + containerInventory.setIngredient(item); + } else if ((fuel == null) || fuel.equals(item)) { + containerInventory.setFuel(item); + } + + } + + private void replaceFurnaceContent(LootTable lootTable, Random random, FurnaceInventory containerInventory) { + ItemStack item = lootTable.getRandomWeightedItem(); + ItemStack result = containerInventory.getResult(); + ItemStack fuel = containerInventory.getFuel(); + ItemStack smelting = containerInventory.getSmelting(); + + if ((result == null) || result.equals(item)) { + containerInventory.setResult(item); + } else if ((fuel == null) || fuel.equals(item)) { + containerInventory.setFuel(item); + } else if ((smelting == null) || smelting.equals(item)) { + containerInventory.setSmelting(item); + } + } + + /** + * Rotate the point around the center. + * + * @param point The point + * @param center The center + * @param angle The angle to rotate by. + * @return The final position. + */ + private BlockVector3 rotateAround(BlockVector3 point, BlockVector3 center, double angle) { + angle = Math.toRadians(angle * -1); + double rotatedX = Math.cos(angle) * (point.getX() - center.getX()) - Math.sin(angle) * (point.getZ() - center.getZ()) + center.getX(); + double rotatedZ = Math.sin(angle) * (point.getX() - center.getX()) + Math.cos(angle) * (point.getZ() - center.getZ()) + center.getZ(); + + return BlockVector3.at(rotatedX, point.getY(), rotatedZ); + } } \ No newline at end of file diff --git a/src/com/ryandw11/structure/api/ConditionType.java b/src/com/ryandw11/structure/api/ConditionType.java deleted file mode 100644 index 426fdce..0000000 --- a/src/com/ryandw11/structure/api/ConditionType.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.ryandw11.structure.api; - -public enum ConditionType { - BIOME("Biome"), - WORLD("AllowedWorlds"), - INAIR("PlaceAir"), - INLIQUID("spawnInLiquid"), - SPAWNY("SpawnY"), - LOOTTABLES("LootTables"), - RANDOMROTATION("randomRotation"), - WHITELIST("whitelistSpawnBlocks"); - - private String configSel; - ConditionType(String configSel){ - this.configSel = configSel; - } - - public String getConfigSel() { - return configSel; - } -} diff --git a/src/com/ryandw11/structure/api/CustomStructuresAPI.java b/src/com/ryandw11/structure/api/CustomStructuresAPI.java index 3770e4f..448db06 100644 --- a/src/com/ryandw11/structure/api/CustomStructuresAPI.java +++ b/src/com/ryandw11/structure/api/CustomStructuresAPI.java @@ -1,15 +1,20 @@ package com.ryandw11.structure.api; -import java.util.Arrays; -import java.util.Set; - -import org.bukkit.configuration.ConfigurationSection; - import com.ryandw11.structure.CustomStructures; +import com.ryandw11.structure.loottables.LootTablesHandler; +import com.ryandw11.structure.loottables.customitems.CustomItemManager; +import com.ryandw11.structure.structure.StructureHandler; public class CustomStructuresAPI { + + private CustomStructures plugin; + public CustomStructuresAPI(){ + this.plugin = CustomStructures.plugin; + } + + public CustomStructures getMainInstance() { - return CustomStructures.plugin; + return plugin; } /** @@ -17,43 +22,30 @@ public CustomStructures getMainInstance() { * @return The number of structures. */ public int getNumberOfStructures() { - Set cs = this.getMainInstance().getConfig().getConfigurationSection("Schematics").getKeys(false); - return cs.size(); + return getStructureHandler().getStructures().size(); } - + /** - * Create a new structure. - *

Note: The Schematic file must already be inside the plugin schematic folder!

- * @param name The name of the strucuture - * @param schematic The name of the schematic file. - * @return The structure that was created. + * Get the structure handler. + * @return The structure handler. */ - public Structure createStructure(String name, String schematic) { - ConfigurationSection cs = this.getMainInstance().getConfig().getConfigurationSection("Schematics." + name); - cs.set("Schematic", schematic); - cs.set("Biome", "all"); - cs.set("Chance.Number", 1); - cs.set("Chance.OutOf", 1000); - cs.set("AllWorlds", true); - cs.set("SpawnY", -1); - cs.set("PlaceAir", true); - cs.set("spawnInLiquid", false); - cs.set("AllowedWorlds", Arrays.asList("world")); - - return new Structure(name); + public StructureHandler getStructureHandler(){ + return plugin.getStructureHandler(); } - + + /** + * Get the loot table handler. + * @return The loot table handler. + */ + public LootTablesHandler getLootTableHandler(){ + return plugin.getLootTableHandler(); + } + /** - * Delete a Structure. - * @param name The name of the structure. - * @throws DeletionForbiddenException If the server has disabled plugins removing structures then this is thrown. + * Get the custom item manager. + * @return The custom item manager. */ - public void deleteStructure(String name) throws DeletionForbiddenException { - if(this.getMainInstance().getConfig().getBoolean("allowDeletion")) { - this.getMainInstance().getConfig().set("Schematics." + name, null); - }else { - this.getMainInstance().getLogger().warning("A plugin has tried to delete a structure! To allow it to delete schematics, change the config option."); - throw new DeletionForbiddenException("This plugin is not allowed to delete structures!"); - } + public CustomItemManager getCustomItemManager(){ + return plugin.getCustomItemManager(); } } diff --git a/src/com/ryandw11/structure/api/Structure.java b/src/com/ryandw11/structure/api/Structure.java deleted file mode 100644 index c9da4d8..0000000 --- a/src/com/ryandw11/structure/api/Structure.java +++ /dev/null @@ -1,106 +0,0 @@ -package com.ryandw11.structure.api; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -import org.bukkit.Location; -import org.bukkit.configuration.ConfigurationSection; - -import com.ryandw11.structure.CustomStructures; -import com.ryandw11.structure.SchematicHandeler; -import com.ryandw11.structure.loottables.LootTable; -import com.ryandw11.structure.utils.RandomCollection; -import com.sk89q.worldedit.WorldEditException; - -public class Structure { - private String name; - private ConfigurationSection cs; - - public Structure(String name) { - this.name = name; - cs = CustomStructures.plugin.getConfig().getConfigurationSection("Schematics." + name); - } - - public String getName() { - return name; - } - - /** - * Get a Condition - * - * @param ct - The Condition wanted - * @return An object based upon the ConditionType entered and what it is set to - * in the config. - */ - public Object getCondition(ConditionType ct) { - switch (ct) { - case BIOME: - return cs.getString("Biome"); - case WORLD: - if (cs.getBoolean("AllWorlds")) - return "all"; - else - return cs.getStringList("AllowedWorlds"); - case INAIR: - return cs.getBoolean("PlaceAir"); - case INLIQUID: - return cs.getBoolean("spawnInLiquid"); - case SPAWNY: - return cs.getString("SpawnY"); - case RANDOMROTATION: - return cs.getBoolean("randomRotation"); - case WHITELIST: - return cs.getStringList("whitelistSpawnBlocks"); - case LOOTTABLES: - RandomCollection lootTables = new RandomCollection<>(); - - for (String name : cs.getConfigurationSection("LootTables").getKeys(true)) { - int weight = cs.getInt("LootTables." + name); - lootTables.add(weight, name); - } - return lootTables; - default: - return null; - } - } - - /** - * Set a condition - * - * @param ct - The condition wanted - * @param set - The Object it is set to. - */ - public void setCondition(ConditionType ct, Object set) { - cs.set(ct.getConfigSel(), set); - } - - /** - * Spawn the structure in. (Ignores the Biome, Liquid, SpawnY, and World - * conditions.) - * - * @param loc The location where the structure should be spawned. - * @throws IOException - * @throws WorldEditException - */ - @SuppressWarnings("unchecked") - public void spawnStructure(Location loc) throws IOException, WorldEditException { - SchematicHandeler sh = new SchematicHandeler(); - sh.schemHandle(loc, name, (boolean) this.getCondition(ConditionType.INAIR), - (RandomCollection) this.getCondition(ConditionType.LOOTTABLES), cs); - } - - /** - * Get the loottables for a structure. - * @return A list of loottables. (Can return null if LootTables does not exist) - */ - public List getLootTables() { - if(!cs.contains("LootTables")) return null; - - List output = new ArrayList(); - for(String table : cs.getConfigurationSection("LootTables").getKeys(false)) { - output.add(new LootTable(table)); - } - return output; - } -} diff --git a/src/com/ryandw11/structure/commands/SCommand.java b/src/com/ryandw11/structure/commands/SCommand.java index 15010f2..e874405 100644 --- a/src/com/ryandw11/structure/commands/SCommand.java +++ b/src/com/ryandw11/structure/commands/SCommand.java @@ -1,113 +1,264 @@ -package com.ryandw11.structure.commands; - -import java.io.IOException; - -import org.bukkit.ChatColor; -import org.bukkit.command.Command; -import org.bukkit.command.CommandExecutor; -import org.bukkit.command.CommandSender; -import org.bukkit.configuration.ConfigurationSection; -import org.bukkit.entity.Player; - -import com.ryandw11.structure.CustomStructures; -import com.ryandw11.structure.SchematicHandeler; -import com.ryandw11.structure.loottables.LootTablesHandler; -import com.ryandw11.structure.utils.CheckLootTables; -import com.ryandw11.structure.utils.CheckSchematics; -import com.ryandw11.structure.utils.RandomCollection; -import com.sk89q.worldedit.WorldEditException; - -public class SCommand implements CommandExecutor { - private CustomStructures plugin; - - public SCommand() { - plugin = CustomStructures.plugin; - } - - @Override - public boolean onCommand(CommandSender sender, Command cmd, String s, String[] args) { - if(!CustomStructures.enabled) { - sender.sendMessage(ChatColor.RED + "One of your schematic or lootable files could not be found!"); - sender.sendMessage(ChatColor.RED + "Please check to see if all of your files are in the proper folders!"); - sender.sendMessage(ChatColor.RED + "To find out more, see the error in the console."); - return true; - } - if (args.length == 1 && args[0].equalsIgnoreCase("reload")) { - if (sender.hasPermission("customstructures.reload")) { - plugin.reloadConfig(); - sender.sendMessage("The plugin has been reloaded!"); - plugin.getLogger().info("Plugin reloaded!"); - - CheckSchematics cs = new CheckSchematics(plugin.getConfig().getConfigurationSection("Schematics").getKeys(false)); - cs.runTaskTimer(plugin, 5L, 1L); - plugin.setStructures(); - - CheckLootTables cl = new CheckLootTables(plugin.getConfig().getConfigurationSection("Schematics").getKeys(false)); - cl.runTaskTimer(plugin, 5L, 1L); - - CustomStructures.lootTablesHandler = new LootTablesHandler(); - } else { - sender.sendMessage(ChatColor.RED + "You do not have permission for this command."); - } - } else if (args.length == 2 && args[0].equalsIgnoreCase("test")) { - if (!(sender instanceof Player)) { - sender.sendMessage("This command is for players only!"); - return true; - } - Player p = (Player) sender; - if (!p.hasPermission("customstructures.test")) { - p.sendMessage(ChatColor.RED + "You do not have permission for this command."); - return true; - } - if (!plugin.getConfig().contains("Schematics." + args[1])) { - p.sendMessage(ChatColor.RED + "That schematic does not exist!"); - return true; - } - SchematicHandeler sh = new SchematicHandeler(); - try { - RandomCollection lootTables = new RandomCollection<>(); - ConfigurationSection lootTablesCS = plugin.getConfig() - .getConfigurationSection("Schematics." + args[1] + ".LootTables"); - if (lootTablesCS != null) { - for (String name : lootTablesCS.getKeys(true)) { - int weight = plugin.getConfig().getInt("Schematics." + args[1] + ".LootTables." + name); - lootTables.add(weight, name); - } - } - - sh.schemHandle(p.getLocation(), plugin.getConfig().getString("Schematics." + args[1] + ".Schematic"), - plugin.getConfig().getBoolean("Schematics." + s + ".PlaceAir"), lootTables, - plugin.getConfig().getConfigurationSection("Schematics." + args[1])); - - } catch (IOException | WorldEditException e) { - e.printStackTrace(); - } - } else if (args.length == 1 && args[0].equalsIgnoreCase("list")) { - sender.sendMessage(ChatColor.GREEN + "Currently Active Schematics:"); - for (String st : plugin.getConfig().getConfigurationSection("Schematics").getKeys(false)) { - sender.sendMessage(ChatColor.GREEN + " - " + ChatColor.BLUE + st); - } - } else { - if (sender.hasPermission("customstructures.info")) { - sender.sendMessage(ChatColor.translateAlternateColorCodes('&', - "&3=============[&2CustomStructures&3]=============")); - sender.sendMessage(ChatColor.translateAlternateColorCodes('&', "&3Created by: &2Ryandw11")); - sender.sendMessage(ChatColor.translateAlternateColorCodes('&', - "&3Version: &2" + plugin.getDescription().getVersion())); - sender.sendMessage(ChatColor.translateAlternateColorCodes('&', - "&3Github wiki:&2 https://github.com/ryandw11/CustomStructures/wiki")); - sender.sendMessage(ChatColor.translateAlternateColorCodes('&', "&3Commands:")); - sender.sendMessage(ChatColor.translateAlternateColorCodes('&', "&3/cs reload - &2Reload the plugin.")); - sender.sendMessage(ChatColor.translateAlternateColorCodes('&', - "&3/cstructure test (name) - &2Paste the defined structure.")); - sender.sendMessage(ChatColor.translateAlternateColorCodes('&', - "&3/cstructure list - &2List the currently active structures.")); - } else { - sender.sendMessage(ChatColor.RED + "You do not have permission for this command."); - } - } - - return false; - } - -} +package com.ryandw11.structure.commands;; + +import com.ryandw11.structure.SchematicHandeler; +import com.ryandw11.structure.structure.Structure; +import com.ryandw11.structure.structure.StructureBuilder; +import com.ryandw11.structure.structure.properties.BlockLevelLimit; +import com.ryandw11.structure.structure.properties.StructureLimitations; +import com.ryandw11.structure.structure.properties.StructureLocation; +import com.ryandw11.structure.structure.properties.StructureProperties; +import com.ryandw11.structure.utils.RandomCollection; +import org.bukkit.ChatColor; +import org.bukkit.Material; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +import com.ryandw11.structure.CustomStructures; +import org.bukkit.inventory.ItemStack; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Objects; + +public class SCommand implements CommandExecutor { + private CustomStructures plugin; + + public SCommand(CustomStructures plugin) { + this.plugin = plugin; + } + + @Override + public boolean onCommand(CommandSender sender, Command cmd, String s, String[] args) { + if(!CustomStructures.enabled) { + sender.sendMessage(ChatColor.RED + "One of your schematic or lootable files could not be found!"); + sender.sendMessage(ChatColor.RED + "Please check to see if all of your files are in the proper folders!"); + sender.sendMessage(ChatColor.RED + "To find out more, see the error in the console."); + return true; + } + if (args.length == 1 && args[0].equalsIgnoreCase("reload")) { + if (sender.hasPermission("customstructures.reload")) { + plugin.reloadConfig(); + sender.sendMessage("The plugin has been reloaded!"); + plugin.getLogger().info("Plugin reloaded!"); + + plugin.reloadHandlers(); + } else { + sender.sendMessage(ChatColor.RED + "You do not have permission for this command."); + } + } else if (args.length == 2 && args[0].equalsIgnoreCase("test")) { + if (!(sender instanceof Player)) { + sender.sendMessage("This command is for players only!"); + return true; + } + Player p = (Player) sender; + if (!p.hasPermission("customstructures.test")) { + p.sendMessage(ChatColor.RED + "You do not have permission for this command."); + return true; + } + Structure structure = plugin.getStructureHandler().getStructure(args[1]); + if(structure == null){ + p.sendMessage(ChatColor.RED + "That schematic does not exist!"); + return true; + } + + structure.spawn(p.getLocation()); + + } else if (args.length == 1 && args[0].equalsIgnoreCase("list")) { + sender.sendMessage(ChatColor.GREEN + "Currently Active Schematics:"); + for (Structure st : plugin.getStructureHandler().getStructures()) { + sender.sendMessage(ChatColor.GREEN + " - " + ChatColor.BLUE + st.getName()); + } + } else if(args.length == 1 && args[0].equalsIgnoreCase("additem")){ + if(!sender.hasPermission("customstructures.additem")){ + sender.sendMessage(ChatColor.RED + "You do not have permission for this command!"); + return true; + } + sender.sendMessage(ChatColor.RED + "You must specify a unique key to call the item by."); + }else if(args.length == 2 && args[0].equalsIgnoreCase("additem")){ + if(!sender.hasPermission("customstructures.additem")){ + sender.sendMessage(ChatColor.RED + "You do not have permission for this command!"); + return true; + } + if(!(sender instanceof Player)){ + sender.sendMessage(ChatColor.RED + "This command is for players only!"); + return true; + } + Player p = (Player) sender; + String key = args[1]; + ItemStack item = p.getInventory().getItemInMainHand(); + item.setAmount(1); + if(item.getType() == Material.AIR){ + p.sendMessage(ChatColor.RED + "You must be holding an item to use this command!"); + return true; + } + if(!plugin.getCustomItemManager().addItem(key, item)){ + p.sendMessage(ChatColor.RED + "That key already exists!"); + }else{ + p.sendMessage(ChatColor.GREEN + "Successfully added the custom item to the list."); + } + }else if(args.length == 1 && args[0].equalsIgnoreCase("checkkey")){ + if(!sender.hasPermission("customstructures.checkkey")){ + sender.sendMessage(ChatColor.RED + "You do not have permission for this command!"); + return true; + } + if(!(sender instanceof Player)){ + sender.sendMessage(ChatColor.RED + "This command is for players only!"); + return true; + } + Player p = (Player) sender; + ItemStack item = p.getInventory().getItemInMainHand(); + if(item.getType() == Material.AIR){ + p.sendMessage(ChatColor.RED + "You must be holding an item to use this command!"); + return true; + } + for(String items : Objects.requireNonNull(plugin.getCustomItemManager().getConfig().getConfigurationSection("")).getKeys(false)){ + if(item.isSimilar(plugin.getCustomItemManager().getItem(items))){ + p.sendMessage(ChatColor.GREEN + "The item you are holding has a key of: " + ChatColor.GOLD + items); + return true; + } + } + p.sendMessage(ChatColor.RED + "That item was not found in the item list."); + }else if(args.length == 1 && args[0].equalsIgnoreCase("getitem")){ + if(!sender.hasPermission("customstructures.getitem")){ + sender.sendMessage(ChatColor.RED + "You do not have permission for this command!"); + return true; + } + sender.sendMessage(ChatColor.RED + "You must specify the key in order to get an item from the list."); + }else if(args.length == 2 && args[0].equalsIgnoreCase("getitem")){ + if(!sender.hasPermission("customstructures.getitem")){ + sender.sendMessage(ChatColor.RED + "You do not have permission for this command!"); + return true; + } + if(!(sender instanceof Player)){ + sender.sendMessage(ChatColor.RED + "This command is for players only!"); + return true; + } + Player p = (Player) sender; + String key = args[1]; + ItemStack item = plugin.getCustomItemManager().getItem(key); + if(item == null){ + p.sendMessage(ChatColor.RED + "An item with that key was not found!"); + return true; + } + p.getInventory().addItem(item); + p.sendMessage(ChatColor.GREEN + "Successfully retrieved that item!"); + }else if(args.length == 1 && args[0].equalsIgnoreCase("createschem")){ + if(!sender.hasPermission("customstructures.createschematic")){ + sender.sendMessage(ChatColor.RED + "You do not have permission for this command!"); + return true; + } + sender.sendMessage(ChatColor.RED + "You must specify the name in order to create a schematic."); + } + else if(args.length == 2 && args[0].equalsIgnoreCase("createschem")){ + if(!sender.hasPermission("customstructures.createschematic")){ + sender.sendMessage(ChatColor.RED + "You do not have permission for this command!"); + return true; + } + if(!(sender instanceof Player)){ + sender.sendMessage(ChatColor.RED + "This command is for players only!"); + return true; + } + Player p = (Player) sender; + String name = args[1].replace(".schem", ""); + SchematicHandeler handeler = new SchematicHandeler(); + if(handeler.createSchematic(name, p, p.getWorld())){ + p.sendMessage(ChatColor.GREEN + "Successfully created a schematic with the name of " + ChatColor.GOLD + name + ChatColor.GREEN + "!"); + p.sendMessage(ChatColor.GREEN + "You can now use " + ChatColor.GOLD + name + ".schem" + ChatColor.GREEN + " in a structure."); + }else{ + p.sendMessage(ChatColor.RED + "The world edit region seems to be incomplete! Try making a selection first!"); + } + } + else if( args.length != 0 && args.length < 3 && args[0].equalsIgnoreCase("create")){ + if(!sender.hasPermission("customstructures.create")){ + sender.sendMessage(ChatColor.RED + "You do not have permission for this command!"); + return true; + } + sender.sendMessage(ChatColor.RED + "You must specify the name and schematic of a structure!"); + } + else if(args.length == 3 && args[0].equalsIgnoreCase("create")){ + if(!sender.hasPermission("customstructures.create")){ + sender.sendMessage(ChatColor.RED + "You do not have permission for this command!"); + return true; + } + String name = args[1]; + String schematic = args[2].replace(".schem", ""); + if(schematic.equals("")){ + sender.sendMessage(ChatColor.RED + "Invalid schematic!"); + return true; + } + if(plugin.getStructureHandler().getStructure(name) != null){ + sender.sendMessage(ChatColor.RED + "A structure with that name already exists!"); + return true; + } + File f = new File(plugin.getDataFolder() + File.separator + "structures" + File.separator + name + ".yml"); + try{ + if(!f.exists()) + f.createNewFile(); + }catch(IOException ex){ + sender.sendMessage(ChatColor.RED + "An error occurred while creating a structure file!"); + plugin.getLogger().severe("Could not create structure file!"); + if(plugin.isDebug()) + ex.printStackTrace(); + return true; + } + StructureBuilder builder = new StructureBuilder(name, schematic + ".schem"); + builder.setChance(1, 1000); + builder.setLootTables(new RandomCollection<>()); + builder.setStructureProperties(new StructureProperties()); + builder.setStructureLocation(new StructureLocation()); + builder.setStructureLimitations(new StructureLimitations(new ArrayList<>(), new BlockLevelLimit(), new HashMap<>())); + try { + builder.save(f); + } catch (IOException e) { + sender.sendMessage(ChatColor.RED + "An error occurred while saving the structure file!"); + plugin.getLogger().severe("Could not save structure file!"); + if(plugin.isDebug()) + e.printStackTrace(); + return true; + } + List structs = plugin.getConfig().getStringList("Structures"); + structs.add(name); + plugin.getConfig().set("Structures", structs); + plugin.saveConfig(); + sender.sendMessage(ChatColor.GREEN + "Successfully created the structure " + ChatColor.GOLD + name + ChatColor.GREEN + "!"); + sender.sendMessage(ChatColor.translateAlternateColorCodes('&', "&aRun the &6/cstructure reload &ato load in the new structure and changes.")); + } + else { + if (sender.hasPermission("customstructures.info")) { + sender.sendMessage(ChatColor.translateAlternateColorCodes('&', + "&3=============[&2CustomStructures&3]=============")); + sender.sendMessage(ChatColor.translateAlternateColorCodes('&', "&3Created by: &2Ryandw11")); + sender.sendMessage(ChatColor.translateAlternateColorCodes('&', + "&3Version: &2" + plugin.getDescription().getVersion())); + sender.sendMessage(ChatColor.translateAlternateColorCodes('&', + "&3Github wiki:&2 https://github.com/ryandw11/CustomStructures/wiki")); + sender.sendMessage(ChatColor.translateAlternateColorCodes('&', "&3Commands:")); + sender.sendMessage(ChatColor.translateAlternateColorCodes('&', "&3/cstructure reload - &2Reload the plugin.")); + sender.sendMessage(ChatColor.translateAlternateColorCodes('&', + "&3/cstructure test (name) - &2Paste the defined structure.")); + sender.sendMessage(ChatColor.translateAlternateColorCodes('&', + "&3/cstructure list - &2List the currently active structures.")); + sender.sendMessage(ChatColor.translateAlternateColorCodes('&', + "&3/cstructure addItem {key} - &2Add an item to the custom items list.")); + sender.sendMessage(ChatColor.translateAlternateColorCodes('&', + "&3/cstructure checkKey - &2Get the key of an item you are holding in your hand.")); + sender.sendMessage(ChatColor.translateAlternateColorCodes('&', + "&3/cstructure getItem {key} - &2Get the item of the key specified.")); + sender.sendMessage(ChatColor.translateAlternateColorCodes('&', + "&3/cstructure createschem {name} - &2Create a schematic from the current worldedit selection (This is automatically save to the CustomStructures schematic folder).")); + sender.sendMessage(ChatColor.translateAlternateColorCodes('&', + "&3/cstructure create {name} {schematic} - &2Create a structure using the default settings.")); + } else { + sender.sendMessage(ChatColor.RED + "You do not have permission for this command."); + } + } + + return false; + } + +} diff --git a/src/com/ryandw11/structure/commands/SCommandTab.java b/src/com/ryandw11/structure/commands/SCommandTab.java new file mode 100644 index 0000000..1750a96 --- /dev/null +++ b/src/com/ryandw11/structure/commands/SCommandTab.java @@ -0,0 +1,37 @@ +package com.ryandw11.structure.commands; + +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.command.TabCompleter; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +public class SCommandTab implements TabCompleter { + @Override + public List onTabComplete(@NotNull CommandSender sender, @NotNull Command cmd, @NotNull String s, String[] args) { + List completions = new ArrayList<>(); + if (args.length < 2) { + completions = new ArrayList<>(Arrays.asList("reload", "test", "list", "additem", "checkkey", "getitem", "createschem", "create")); + completions = getAppliableTabCompleters(args.length == 1 ? args[0] : "", completions); + } + Collections.sort(completions); + return completions; + } + + public List getAppliableTabCompleters(String arg, List completions) { + if (arg == null || arg.equalsIgnoreCase("")) { + return completions; + } + ArrayList valid = new ArrayList<>(); + for (String posib : completions) { + if (posib.startsWith(arg)) { + valid.add(posib); + } + } + return valid; + } +} diff --git a/src/com/ryandw11/structure/listener/ChunkLoad.java b/src/com/ryandw11/structure/listener/ChunkLoad.java index d7422d8..819a1b0 100644 --- a/src/com/ryandw11/structure/listener/ChunkLoad.java +++ b/src/com/ryandw11/structure/listener/ChunkLoad.java @@ -12,47 +12,43 @@ /** * Class for when a chunk loads. - * @author Ryandw11 * + * @author Ryandw11 */ -public class ChunkLoad implements Listener{ - - //private CustomStructures plugin; - - public ChunkLoad(){ - //this.plugin = CustomStructures.plugin; - } - - @EventHandler - public void loadevent(ChunkLoadEvent e){ - if(!CustomStructures.enabled) return; - - if(e.isNewChunk()){ //Checks to see if the chunk is new or an old one. - World w = e.getChunk().getWorld(); //Grabs the world - Block b = e.getChunk().getBlock(0, 5, 0); //Grabs the block 0, 5, 0 in that chunk. - - if(w.getHighestBlockYAt(b.getX(), b.getZ()) == -1) return; - - boolean foundLand = false; //True when the block selected is an ideal place for a structure. - Block bb = e.getChunk().getBlock(0, w.getHighestBlockYAt(b.getX(), b.getZ()), 0); //grabs the highest block in that chunk at X = 0 and Z = 0 for that chunk. - int trys = 0; - while (!foundLand){//While land was not found it keeps checking. - if(trys >= 20) return; //added anti crash. - if(bb.getType() != Material.AIR){ - foundLand = true; - } - else{ - bb = bb.getLocation().subtract(0, 1, 0).getBlock(); - } - trys++; - } - - /* - * Schematic handeler - * Runs a BukkitRunnable to prevent the server from crashing. - */ - StructurePicker s = new StructurePicker(bb, e.getChunk()); - s.runTaskTimer(CustomStructures.plugin, 1, 10); - } - } +public class ChunkLoad implements Listener { + + public ChunkLoad() { + } + + @EventHandler + public void onChunkLoad(ChunkLoadEvent e) { + if (!CustomStructures.enabled) return; + + if (!e.isNewChunk()) return; + + + World w = e.getChunk().getWorld(); //Grabs the world + Block b = e.getChunk().getBlock(0, 5, 0); //Grabs the block 0, 5, 0 in that chunk. + + boolean foundLand = false; //True when the block selected is an ideal place for a structure. + if (w.getHighestBlockYAt(b.getX(), b.getZ()) == -1) return; + Block bb = e.getChunk().getBlock(0, w.getHighestBlockYAt(b.getX(), b.getZ()), 0); //grabs the highest block in that chunk at X = 0 and Z = 0 for that chunk. + int trys = 0; + while (!foundLand) {//While land was not found it keeps checking. + if (trys >= 20) return; //added anti crash. + if (bb.getType() != Material.AIR) { + foundLand = true; + } else { + bb = bb.getLocation().subtract(0, 1, 0).getBlock(); + } + trys++; + } + + /* + * Schematic handler + * This activity is done async to prevent the server from lagging. + */ + StructurePicker s = new StructurePicker(bb, e.getChunk(), CustomStructures.getInstance()); + s.runTaskTimer(CustomStructures.plugin, 1, 10); + } } diff --git a/src/com/ryandw11/structure/loottables/LootItem.java b/src/com/ryandw11/structure/loottables/LootItem.java index c94405e..82476c7 100644 --- a/src/com/ryandw11/structure/loottables/LootItem.java +++ b/src/com/ryandw11/structure/loottables/LootItem.java @@ -18,6 +18,14 @@ public class LootItem { private int weight; private ItemStack item; + /** + * This is for normal loottable items. + * @param customName The custom name. + * @param type The type. + * @param amount The amount. + * @param weight The weight. + * @param enchants The enchants. + */ public LootItem(String customName, String type, int amount, int weight, Map enchants) { this.weight = weight; this.item = new ItemStack(Material.valueOf(type)); @@ -35,6 +43,18 @@ public LootItem(String customName, String type, int amount, int weight, Map randomCollection; + private String name; public FileConfiguration lootTablesFC; public LootTable(String name) { this.LoadFile(name); + this.name = name; this.type = LootTableType.valueOf(this.lootTablesFC.getString("Type")); this.rolls = this.lootTablesFC.getInt("Rolls"); @@ -41,27 +43,38 @@ public LootTable(String name) { private void loadItems() { - this.randomCollection = new RandomCollection(); + this.randomCollection = new RandomCollection<>(); for (String itemID : this.lootTablesFC.getConfigurationSection("Items").getKeys(false)) { - - String customName = this.lootTablesFC.getString("Items." + itemID + ".Name"); - String type = this.lootTablesFC.getString("Items." + itemID + ".Type"); - int amount = this.lootTablesFC.getInt("Items." + itemID + ".Amount"); - int weight = this.lootTablesFC.getInt("Items." + itemID + ".Weight"); - Map enchants = new HashMap<>(); - - ConfigurationSection enchantMents = this.lootTablesFC - .getConfigurationSection("Items." + itemID + ".Enchantments"); - - if (enchantMents != null) { - for (String enchantName : enchantMents.getKeys(false)) { - int level = this.lootTablesFC.getInt("Items." + itemID + ".Enchantments." + enchantName); - enchants.put(enchantName, level); + if(lootTablesFC.getString("Items." + itemID + ".Type").equalsIgnoreCase("CUSTOM")){ + int amount = this.lootTablesFC.getInt("Items." + itemID + ".Amount"); + int weight = this.lootTablesFC.getInt("Items." + itemID + ".Weight"); + ItemStack item = CustomStructures.getInstance().getCustomItemManager().getItem(this.lootTablesFC.getString("Items." + itemID + ".Key")); + if(item == null){ + CustomStructures.getInstance().getLogger().warning("Cannot find a custom item with the id of " + itemID + + " in the " + name + " loot table!"); + continue; + } + this.randomCollection.add(weight, new LootItem(item, amount, weight)); + }else{ + String customName = this.lootTablesFC.getString("Items." + itemID + ".Name"); + String type = this.lootTablesFC.getString("Items." + itemID + ".Type"); + int amount = this.lootTablesFC.getInt("Items." + itemID + ".Amount"); + int weight = this.lootTablesFC.getInt("Items." + itemID + ".Weight"); + Map enchants = new HashMap<>(); + + ConfigurationSection enchantMents = this.lootTablesFC + .getConfigurationSection("Items." + itemID + ".Enchantments"); + + if (enchantMents != null) { + for (String enchantName : enchantMents.getKeys(false)) { + int level = this.lootTablesFC.getInt("Items." + itemID + ".Enchantments." + enchantName); + enchants.put(enchantName, level); + } } - } - this.randomCollection.add(weight, new LootItem(customName, type, amount, weight, enchants)); + this.randomCollection.add(weight, new LootItem(customName, type, amount, weight, enchants)); + } } } @@ -95,6 +108,14 @@ public void setType(LootTableType type) { this.type = type; } + /** + * Get the name of the loottable. + * @return The name of the loottable. + */ + public String getName(){ + return name; + } + /** * Get the number of items choosen. * @return Number of items choosen diff --git a/src/com/ryandw11/structure/loottables/customitems/CustomItemManager.java b/src/com/ryandw11/structure/loottables/customitems/CustomItemManager.java new file mode 100644 index 0000000..ccfba24 --- /dev/null +++ b/src/com/ryandw11/structure/loottables/customitems/CustomItemManager.java @@ -0,0 +1,100 @@ +package com.ryandw11.structure.loottables.customitems; + +import com.ryandw11.structure.CustomStructures; +import org.bukkit.Bukkit; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.inventory.ItemStack; + +import java.io.File; +import java.io.IOException; + +public class CustomItemManager { + private FileConfiguration config; + private File file; + private CustomStructures structures; + + /** + * This should only ever be constructed by the CustomStructures main class. + *

Use {@link CustomStructures#getCustomItemManager()} to access this class for the plugin.

+ * @param structures The main class. + * @param file The file. + */ + public CustomItemManager(CustomStructures structures, File file, File dir){ + if(!dir.exists()) + dir.mkdir(); + if(!file.exists()) { + try{ + file.createNewFile(); + }catch(IOException ex){ + structures.getLogger().severe("Cannot create Custom Items file. Enable debug mode for more information."); +// if(structures.isDebug()) + ex.printStackTrace(); + return; + } + } + this.file = file; + this.config = YamlConfiguration.loadConfiguration(file); + this.structures = structures; + } + + /** + * Add an item to the custom items file. + * @param key The key to add. + * @param itemStack The item stack to add. + * @return If the item was successfully added. + *

This returns false if the key already exists in the file.

+ */ + public boolean addItem(String key, ItemStack itemStack){ + if(this.config.contains(key)) + return false; + config.set(key, itemStack.clone()); + try{ + config.save(file); + return true; + }catch(IOException ex){ + structures.getLogger().severe("Failed to save Custom Items file after adding an item."); + if(structures.isDebug()){ + ex.printStackTrace(); + } + return false; + } + } + + /** + * Remove an item from the custom items file. + * @param key The key to remove. + * @return If the key was successfully removed. + *

If the key is not in the file than false is returned.

+ */ + public boolean removeItem(String key){ + if(!this.config.contains(key)) + return false; + config.set(key, null); + try{ + config.save(file); + return true; + }catch(IOException ex){ + structures.getLogger().severe("Failed to save Custom Items file after removing an item."); + if(structures.isDebug()){ + ex.printStackTrace(); + } + return false; + } + } + + /** + * Get an item from the item file. + * @param key The key to check. + * @return The item stack. (Will return null if the item does not exist). + */ + public ItemStack getItem(String key){ + if(!this.config.contains(key)) + return null; + return config.getItemStack(key); + } + + public FileConfiguration getConfig(){ + return config; + } +} diff --git a/src/com/ryandw11/structure/structure/Structure.java b/src/com/ryandw11/structure/structure/Structure.java new file mode 100644 index 0000000..e49b608 --- /dev/null +++ b/src/com/ryandw11/structure/structure/Structure.java @@ -0,0 +1,109 @@ +package com.ryandw11.structure.structure; + +import com.ryandw11.structure.SchematicHandeler; +import com.ryandw11.structure.loottables.LootTable; +import com.ryandw11.structure.structure.properties.StructureLimitations; +import com.ryandw11.structure.structure.properties.StructureLocation; +import com.ryandw11.structure.structure.properties.StructureProperties; +import com.ryandw11.structure.utils.RandomCollection; +import com.sk89q.worldedit.WorldEditException; +import org.bukkit.Bukkit; +import org.bukkit.Chunk; +import org.bukkit.Location; +import org.bukkit.block.Block; + +import java.io.IOException; +import java.util.List; +import java.util.concurrent.ThreadLocalRandom; + +/** + * The main class when it comes to structures. + */ +public class Structure { + private final String name; + private final String schematic; + private final int chanceNumber; + private final int chanceOutOf; + private final StructureLocation structureLocation; + private final StructureProperties structureProperties; + private final StructureLimitations structureLimitations; + private final RandomCollection lootTables; + + protected Structure( StructureBuilder builder){ + this.name = builder.name; + this.schematic = builder.schematic; + this.chanceNumber = builder.chanceNumber; + this.chanceOutOf = builder.chanceOutOf; + this.structureLocation = builder.structureLocation; + this.structureProperties = builder.structureProperties; + this.structureLimitations = builder.structureLimitations; + this.lootTables = builder.lootTables; + } + + public String getName(){ + return name; + } + + public String getSchematic(){ + return schematic; + } + + public int getChanceNumber(){ + return chanceNumber; + } + + public int getChanceOutOf(){ + return chanceOutOf; + } + + public StructureLocation getStructureLocation(){ + return structureLocation; + } + + public StructureProperties getStructureProperties(){ + return structureProperties; + } + + public StructureLimitations getStructureLimitations() { + return structureLimitations; + } + + public RandomCollection getLootTables(){ + return this.lootTables; + } + + /** + * Checks to see if the structure can spawn. + *

This also checks structure locations.

+ * @return If the structure can spawn + */ + public boolean canSpawn(Block block, Chunk chunk){ + // Check to see if the structure can spawn in the current world. + if(!getStructureLocation().getWorlds().isEmpty()){ + if(!getStructureLocation().getWorlds().contains(chunk.getWorld().getName())) + return false; + } + + // Check to see if the structure has the chance to spawn + if( ThreadLocalRandom.current().nextInt(getChanceNumber(), getChanceOutOf()) != getChanceNumber()) + return false; + + // Check to see if the structure can spawn in the current biome. + return getStructureLocation().hasBiome(block.getBiome()); + + // TODO add in support for block conditions + } + + /** + * Spawn the schematic at the given location. + * @param location The location to spawn it at. + */ + public void spawn(Location location){ + SchematicHandeler handler = new SchematicHandeler(); + try { + handler.schemHandle(location, getSchematic(), getStructureProperties().canPlaceAir(), getLootTables(), this); + }catch(IOException | WorldEditException ex){ + ex.printStackTrace(); + } + } +} diff --git a/src/com/ryandw11/structure/structure/StructureBuilder.java b/src/com/ryandw11/structure/structure/StructureBuilder.java new file mode 100644 index 0000000..e4ab8de --- /dev/null +++ b/src/com/ryandw11/structure/structure/StructureBuilder.java @@ -0,0 +1,162 @@ +package com.ryandw11.structure.structure; + +import com.ryandw11.structure.CustomStructures; +import com.ryandw11.structure.loottables.LootTable; +import com.ryandw11.structure.structure.properties.StructureLimitations; +import com.ryandw11.structure.structure.properties.StructureLocation; +import com.ryandw11.structure.structure.properties.StructureProperties; +import com.ryandw11.structure.utils.RandomCollection; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; + +import java.io.File; +import java.io.IOException; +import java.util.List; +import java.util.Map; + +public class StructureBuilder { + + private FileConfiguration config; + + protected String name; + protected String schematic; + protected int chanceNumber; + protected int chanceOutOf; + protected StructureLocation structureLocation; + protected StructureProperties structureProperties; + protected StructureLimitations structureLimitations; + protected RandomCollection lootTables; + + private boolean invalid; + + public StructureBuilder(String name, String schematic){ + this.name = name; + this.schematic = schematic; + lootTables = new RandomCollection<>(); + } + + + public StructureBuilder(String name, File file){ + if(!file.exists()) + throw new RuntimeException("Cannot build structure: That file does not exist!"); + config = YamlConfiguration.loadConfiguration(file); + + invalid = false; + + this.name = name; + + if(!checkValidity()) + return; + + schematic = config.getString("schematic"); + chanceNumber = config.getInt("Chance.Number"); + chanceOutOf = config.getInt("Chance.OutOf"); + + structureLocation = new StructureLocation(this, config); + structureProperties = new StructureProperties(config); + structureLimitations = new StructureLimitations(config); + lootTables = new RandomCollection<>(); + if(config.contains("LootTables")){ + ConfigurationSection lootableConfig = config.getConfigurationSection("LootTables"); + assert lootableConfig != null; + for (String lootTable : lootableConfig.getKeys(false)) { + int weight = lootableConfig.getInt(lootTable); + lootTables.add(weight, CustomStructures.getInstance().getLootTableHandler().getLootTableByName(lootTable)); + } + } + } + + private boolean checkValidity(){ + if(!config.contains("schematic")){ + CustomStructures.getInstance().getLogger().severe("Invalid Structure format for:" + config.getName()); + CustomStructures.getInstance().getLogger().severe("Schematic is mandatory, please add one in for this file to be valid."); + setInvalid(); + return false; + } + + if(!config.contains("Chance.Number")){ + CustomStructures.getInstance().getLogger().severe("Invalid Structure format for:" + config.getName()); + CustomStructures.getInstance().getLogger().severe("Chance.Number is mandatory, please add one in for this file to be valid."); + setInvalid(); + return false; + } + if(!config.contains("Chance.OutOf")){ + CustomStructures.getInstance().getLogger().severe("Invalid Structure format for:" + config.getName()); + CustomStructures.getInstance().getLogger().severe("Chance.OutOf is mandatory, please add one in for this file to be valid."); + setInvalid(); + return false; + } + return true; + } + + public void setChance(int number, int outOf){ + this.chanceNumber = number; + this.chanceOutOf = outOf; + } + + public void setStructureLimitations(StructureLimitations limitations){ + this.structureLimitations = limitations; + } + + public void setStructureProperties(StructureProperties properties){ + this.structureProperties = properties; + } + + public void setStructureLocation(StructureLocation location){ + this.structureLocation = location; + } + + public void setLootTables(ConfigurationSection lootableConfig){ + lootTables = new RandomCollection<>(); + assert lootableConfig != null; + for (String lootTable : lootableConfig.getKeys(false)) { + int weight = lootableConfig.getInt(lootTable); + lootTables.add(weight, CustomStructures.getInstance().getLootTableHandler().getLootTableByName(lootTable)); + } + } + + public void setLootTables(RandomCollection lootTables){ + this.lootTables = lootTables; + } + + public void setInvalid(){ + invalid = true; + } + + /** + * Build the structure. + * @return The structure. (Null if the file is invalid). + */ + public Structure build(){ + if(invalid) + return null; + return new Structure(this); + } + + public void save(File file) throws IOException { + file.createNewFile(); + config = YamlConfiguration.loadConfiguration(file); + config.set("schematic", schematic); + config.set("Chance.Number", chanceNumber); + config.set("Chance.OutOf", chanceOutOf); + + config.set("StructureLocation.Worlds", structureLocation.getWorlds()); + config.set("StructureLocation.SpawnY", structureLocation.getSpawnSettings().getValue()); + config.set("StructureLocation.Biome", structureLocation.getBiomes()); + + config.set("StructureProperties.PlaceAir", structureProperties.canPlaceAir()); + config.set("StructureProperties.randomRotation", structureProperties.isRandomRotation()); + config.set("StructureProperties.ignorePlants", structureProperties.isIgnoringPlants()); + config.set("StructureProperties.spawnInWater", structureProperties.canSpawnInWater()); + config.set("StructureProperties.spawnInLavaLakes", structureProperties.canSpawnInLavaLakes()); + + config.set("StructureLimitations.whitelistSpawnBlocks", structureLimitations.getWhitelistBlocks()); + + for(Map.Entry entry : lootTables.getMap().entrySet()){ + config.set("LootTables." + entry.getValue().getName(), entry.getKey()); + } + config.save(file); + } + +} diff --git a/src/com/ryandw11/structure/structure/StructureHandler.java b/src/com/ryandw11/structure/structure/StructureHandler.java new file mode 100644 index 0000000..686eb95 --- /dev/null +++ b/src/com/ryandw11/structure/structure/StructureHandler.java @@ -0,0 +1,64 @@ +package com.ryandw11.structure.structure; + +import com.ryandw11.structure.CustomStructures; + +import java.io.File; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +/** + * Handles the active structures. + */ +public class StructureHandler { + + private List structures; + + public StructureHandler(List stringStructs, CustomStructures cs){ + structures = new ArrayList<>(); + cs.getLogger().info("Loading structures from files."); + for(String s : stringStructs){ + File struct = new File(cs.getDataFolder() + File.separator + "structures" + File.separator + s.replace(".yml", "") + ".yml"); + if(!struct.exists()){ + cs.getLogger().warning("Structure file: " + s + ".yml does not exist! Did you make a new structure file in the Structure folder?"); + cs.getLogger().warning("For more information please check to wiki."); + continue; + } + Structure tempStruct = new StructureBuilder(s.replace(".yml", ""), struct).build(); + if(tempStruct == null) + continue; + structures.add(tempStruct); + } + } + + /** + * Get the list of structures. + *

This list is read only and cannot be modified.

+ * @return The list of structures. + */ + public List getStructures(){ + return Collections.unmodifiableList(structures); + } + + /** + * Get structure by name + * @param name The name + * @return The structure. (Returns null if the structure is not found). + */ + public Structure getStructure(String name){ + List result = structures.stream().filter(struct -> struct.getName().equals(name)).collect(Collectors.toList()); + if(result.isEmpty()) + return null; + return result.get(0); + } + + /** + * Get the structure by a number. + * @param i The number + * @return The structure. + */ + public Structure getStructure(int i){ + return structures.get(i); + } +} diff --git a/src/com/ryandw11/structure/structure/properties/BlockLevelLimit.java b/src/com/ryandw11/structure/structure/properties/BlockLevelLimit.java new file mode 100644 index 0000000..c0970f4 --- /dev/null +++ b/src/com/ryandw11/structure/structure/properties/BlockLevelLimit.java @@ -0,0 +1,58 @@ +package com.ryandw11.structure.structure.properties; + +import org.bukkit.configuration.file.FileConfiguration; + +public class BlockLevelLimit { + private String mode; + + private int x1, z1, x2, z2; + + public BlockLevelLimit(String mode, int x1, int z1, int x2, int z2){ + this.mode = mode; + this.x1 = x1; + this.z1 = z1; + this.x2 = x2; + this.z2 = z2; + } + + public BlockLevelLimit(){ + mode = "NONE"; + } + + public BlockLevelLimit(FileConfiguration fileConfiguration){ + if(!fileConfiguration.contains("StructureLimitations.BlockLevelLimit")){ + mode = "NONE"; + return; + } + + this.mode = fileConfiguration.getString("StructureLimitations.BlockLevelLimit.mode"); + this.x1 = fileConfiguration.getInt("StructureLimitations.BlockLevelLimit.cornerOne.x"); + this.z1 = fileConfiguration.getInt("StructureLimitations.BlockLevelLimit.cornerOne.z"); + this.x2 = fileConfiguration.getInt("StructureLimitations.BlockLevelLimit.cornerTwo.x"); + this.z2 = fileConfiguration.getInt("StructureLimitations.BlockLevelLimit.cornerTwo.z"); + } + + public boolean isEnabled(){ + return mode.equalsIgnoreCase("none"); + } + + public String getMode(){ + return mode; + } + + public int getX1(){ + return x1; + } + + public int getX2(){ + return x2; + } + + public int getZ1(){ + return z1; + } + + public int getZ2(){ + return z2; + } +} diff --git a/src/com/ryandw11/structure/structure/properties/StructureLimitations.java b/src/com/ryandw11/structure/structure/properties/StructureLimitations.java new file mode 100644 index 0000000..e29818f --- /dev/null +++ b/src/com/ryandw11/structure/structure/properties/StructureLimitations.java @@ -0,0 +1,59 @@ +package com.ryandw11.structure.structure.properties; + +import org.bukkit.Material; +import org.bukkit.block.Block; +import org.bukkit.configuration.file.FileConfiguration; + +import java.util.*; + +public class StructureLimitations { + + private List whitelistSpawnBlocks; + private BlockLevelLimit blockLevelLimit; + private Map blockReplacement; + + public StructureLimitations(FileConfiguration configuration){ + if(!configuration.contains("StructureLimitations.whitelistSpawnBlocks")) + whitelistSpawnBlocks = new ArrayList<>(); + else + whitelistSpawnBlocks = configuration.getStringList("StructureLimitations.whitelistSpawnBlocks"); + + this.blockLevelLimit = new BlockLevelLimit(configuration); + + blockReplacement = new HashMap<>(); + if(configuration.contains("StructureLimitations.replacement_blocks")){ + for(String s : Objects.requireNonNull(configuration.getConfigurationSection("StructureLimitations.replacement_blocks")).getKeys(false)){ + Material firstMaterial = Material.getMaterial(s); + Material secondMaterial = Material.getMaterial(Objects.requireNonNull(configuration.getString("StructureLimitations.replacement_blocks." + s))); + blockReplacement.put(firstMaterial, secondMaterial); + } + } + } + + public StructureLimitations(List whitelistSpawnBlocks, BlockLevelLimit blockLevelLimit, Map blockReplacement){ + this.whitelistSpawnBlocks = whitelistSpawnBlocks; + this.blockLevelLimit = blockLevelLimit; + this.blockReplacement = blockReplacement; + } + + public List getWhitelistBlocks(){ + return whitelistSpawnBlocks; + } + + public boolean hasBlock(Block b){ + if(whitelistSpawnBlocks.isEmpty()) return true; + for(String block : whitelistSpawnBlocks){ + if(block.equalsIgnoreCase(b.getType().toString())) + return true; + } + return false; + } + + public BlockLevelLimit getBlockLevelLimit(){ + return blockLevelLimit; + } + + public Map getBlockReplacement(){ + return blockReplacement; + } +} diff --git a/src/com/ryandw11/structure/structure/properties/StructureLocation.java b/src/com/ryandw11/structure/structure/properties/StructureLocation.java new file mode 100644 index 0000000..391ca73 --- /dev/null +++ b/src/com/ryandw11/structure/structure/properties/StructureLocation.java @@ -0,0 +1,82 @@ +package com.ryandw11.structure.structure.properties; + +import com.ryandw11.structure.CustomStructures; +import com.ryandw11.structure.structure.StructureBuilder; +import org.bukkit.block.Biome; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.file.FileConfiguration; + +import java.util.ArrayList; +import java.util.List; + +public class StructureLocation { + + private List worlds; + private StructureYSpawning spawnY; + private List biomes; + + public StructureLocation(StructureBuilder sb, FileConfiguration configuration){ + ConfigurationSection cs = configuration.getConfigurationSection("StructureLocation"); + if(cs == null){ + CustomStructures.getInstance().getLogger().severe("Invalid Structure format for:" + configuration.getName()); + CustomStructures.getInstance().getLogger().severe("StructureLocation is mandatory, please add one in for this file to be valid."); + sb.setInvalid(); + return; + } + if(cs.contains("Worlds")) + this.worlds = cs.getStringList("Worlds"); + else + this.worlds = new ArrayList<>(); + this.spawnY = new StructureYSpawning(configuration); + if(cs.contains("Biomes")) + this.biomes = configuration.getStringList("Biomes"); + else + this.biomes = new ArrayList<>(); + } + + public StructureLocation(List worlds, StructureYSpawning spawnSettings, List biomes){ + this.worlds = worlds; + this.spawnY = spawnSettings; + this.biomes = biomes; + } + + public StructureLocation(){ + this.worlds = new ArrayList<>(); + this.spawnY = new StructureYSpawning("top"); + this.biomes = new ArrayList<>(); + } + + public List getWorlds(){ + return worlds; + } + + public StructureYSpawning getSpawnSettings(){ + return spawnY; + } + + public List getBiomes(){ + return biomes; + } + + public void setWorlds(List worlds){ + this.worlds = worlds; + } + + public void setSpawnSettings(StructureYSpawning spawnY){ + this.spawnY = spawnY; + } + + public void setBiomes(List biomes){ + this.biomes = biomes; + } + + public boolean hasBiome(Biome b){ + if(biomes.isEmpty()) + return true; + for(String biome : biomes){ + if(b.toString().toLowerCase().equals(biome.toLowerCase())) + return true; + } + return false; + } +} diff --git a/src/com/ryandw11/structure/structure/properties/StructureProperties.java b/src/com/ryandw11/structure/structure/properties/StructureProperties.java new file mode 100644 index 0000000..82dad81 --- /dev/null +++ b/src/com/ryandw11/structure/structure/properties/StructureProperties.java @@ -0,0 +1,78 @@ +package com.ryandw11.structure.structure.properties; + +import com.ryandw11.structure.structure.StructureBuilder; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.file.FileConfiguration; + +public class StructureProperties { + + private boolean placeAir; + private boolean randomRotation; + private boolean ignorePlants; + private boolean spawnInWater; + private boolean spawnInLavaLakes; + + public StructureProperties(FileConfiguration configuration){ + ConfigurationSection cs = configuration.getConfigurationSection("StructureProperties"); + if(cs == null){ + this.placeAir = true; + this.randomRotation = false; + this.ignorePlants = true; + this.spawnInWater = true; + this.spawnInLavaLakes = true; + return; + } + this.placeAir = cs.contains("PlaceAir") && cs.getBoolean("PlaceAir"); + this.randomRotation = cs.contains("randomRotation") && cs.getBoolean("randomRotation"); + this.ignorePlants = cs.contains("ignorePlants") && cs.getBoolean("ignorePlants"); + this.spawnInWater = cs.contains("spawnInWater") && cs.getBoolean("spawnInWater"); + this.spawnInLavaLakes = cs.contains("spawnInLavaLakes") && cs.getBoolean("spawnInLavaLakes"); + } + + public StructureProperties(){ + this.placeAir = true; + this.randomRotation = false; + this.ignorePlants = true; + this.spawnInWater = true; + this.spawnInLavaLakes = true; + } + + public boolean canPlaceAir(){ + return placeAir; + } + public void setPlaceAir(boolean placeAir){ + this.placeAir = placeAir; + } + + public boolean isRandomRotation(){ + return randomRotation; + } + + public void setRandomRotation(boolean randomRotation){ + this.randomRotation = randomRotation; + } + + public boolean isIgnoringPlants(){ + return ignorePlants; + } + + public void setIgnorePlants(boolean ignorePlants){ + this.ignorePlants = ignorePlants; + } + + public boolean canSpawnInWater(){ + return spawnInWater; + } + + public void setSpawnInWater(boolean spawnInWater){ + this.spawnInWater = spawnInWater; + } + + public boolean canSpawnInLavaLakes(){ + return spawnInLavaLakes; + } + + public void setSpawnInLavaLakes(boolean spawnInLavaLakes){ + this.spawnInLavaLakes = spawnInLavaLakes; + } +} diff --git a/src/com/ryandw11/structure/structure/properties/StructureYSpawning.java b/src/com/ryandw11/structure/structure/properties/StructureYSpawning.java new file mode 100644 index 0000000..b19d515 --- /dev/null +++ b/src/com/ryandw11/structure/structure/properties/StructureYSpawning.java @@ -0,0 +1,129 @@ +package com.ryandw11.structure.structure.properties; + +import org.bukkit.configuration.file.FileConfiguration; + +import java.util.Random; +import java.util.concurrent.ThreadLocalRandom; + +public class StructureYSpawning { + + private boolean top = false; + private boolean oceanFloor = false; + private String value; + + public StructureYSpawning(FileConfiguration fc){ + value = fc.getString("StructureLocation.SpawnY"); + + assert value != null; + if(value.equalsIgnoreCase("ocean_floor")) + oceanFloor = true; + else if(value.equalsIgnoreCase("top")) + top = true; + } + + public StructureYSpawning(String value){ + this.value = value; + if(value.equalsIgnoreCase("ocean_floor")) + oceanFloor = true; + else if(value.equalsIgnoreCase("top")) + top = true; + } + + public String getValue(){ + return value; + } + + public boolean isTop() { + return top; + } + + public boolean isOceanFloor(){ + return oceanFloor; + } + + public int getHeight (int currentHeight) { + if(top) return currentHeight; + if(value.contains("[")) { + //If +[num-num] + if(value.startsWith("+")) { + String v = value.replace("[", "").replace("]", "").replace("+", ""); + String[] out = v.split("-"); + try { + int num1 = Integer.parseInt(out[0]); + int num2 = Integer.parseInt(out[1]); + + Random r = new Random(); + + int a = r.nextInt(num2) + (num1 + 1); + return currentHeight + a; + + }catch(NumberFormatException | ArrayIndexOutOfBoundsException ex) { + return currentHeight; + } + } + // if -[num-num] + else if(value.startsWith("-")) { + String v = value.replace("[", "").replace("]", "").replace("-", ""); + String[] out = v.split("-"); + try { + int num1 = Integer.parseInt(out[0]); + int num2 = Integer.parseInt(out[1]); + + Random r = new Random(); + + int a = r.nextInt(num2) + (num1 + 1); + return currentHeight - a; + + }catch(NumberFormatException | ArrayIndexOutOfBoundsException ex) { + return currentHeight; + } + } + // if just [num-num] + else { + String v = value.replace("[", "").replace("]", ""); + String[] out = v.split("-"); + try { + int num1 = Integer.parseInt(out[0]); + int num2 = Integer.parseInt(out[1]); + + //Random r = new Random(); + //return r.nextInt(num2 + 1) + num1; + + return ThreadLocalRandom.current().nextInt(num1, num2 + 1); + + }catch(NumberFormatException | ArrayIndexOutOfBoundsException ex) { + return currentHeight; + } + } + } + // if +num + else if(value.startsWith("+")) { + String v = value.replace("+", ""); + try { + int num = Integer.parseInt(v); + return currentHeight + num; + }catch(NumberFormatException ex) { + return currentHeight; + } + } + // if -num + else if(value.startsWith("-")) { + String v = value.replace("-", ""); + + try { + int num = Integer.parseInt(v); + return currentHeight - num; + }catch(NumberFormatException ex) { + return currentHeight; + } + } + // if just num + else { + try { + return Integer.parseInt(value); + } catch(NumberFormatException ex) { + return currentHeight; + } + } + } +} diff --git a/src/com/ryandw11/structure/utils/CSConstants.java b/src/com/ryandw11/structure/utils/CSConstants.java index ac4b337..4d0e5ad 100644 --- a/src/com/ryandw11/structure/utils/CSConstants.java +++ b/src/com/ryandw11/structure/utils/CSConstants.java @@ -13,7 +13,7 @@ * */ public class CSConstants { - public static List leafBlocks = new ArrayList<>(Arrays.asList(Material.GRASS, Material.ACACIA_LEAVES, Material.BIRCH_LEAVES, + public static List plantBlocks = new ArrayList<>(Arrays.asList(Material.GRASS, Material.ACACIA_LEAVES, Material.BIRCH_LEAVES, Material.DARK_OAK_LEAVES, Material.JUNGLE_LEAVES, Material.OAK_LEAVES, Material.SPRUCE_LEAVES, Material.VINE, Material.ACACIA_LOG, - Material.BIRCH_LOG, Material.DARK_OAK_LOG, Material.JUNGLE_LOG, Material.OAK_LOG, Material.SPRUCE_LOG, Material.TALL_GRASS)); + Material.BIRCH_LOG, Material.DARK_OAK_LOG, Material.JUNGLE_LOG, Material.OAK_LOG, Material.SPRUCE_LOG)); } diff --git a/src/com/ryandw11/structure/utils/CheckSchematics.java b/src/com/ryandw11/structure/utils/CheckSchematics.java deleted file mode 100644 index 3ba924a..0000000 --- a/src/com/ryandw11/structure/utils/CheckSchematics.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.ryandw11.structure.utils; - -import java.io.File; -import java.util.ArrayList; -import java.util.Set; - -import org.bukkit.scheduler.BukkitRunnable; - -import com.ryandw11.structure.CustomStructures; - -public class CheckSchematics extends BukkitRunnable{ - private int num; - ArrayList schematics; - private CustomStructures plugin; - String ex = "Schematics."; - public CheckSchematics(Set schematics){ - plugin = CustomStructures.plugin; - num = 0; - ArrayList ls = new ArrayList<>(); - for(String s : schematics) { - ls.add(s); - } - this.schematics = ls; - } - - @Override - public void run() { - if(num >= schematics.size()){ - plugin.getLogger().info("The schematics have been checked! All seems to be in order!"); - this.cancel(); - return; - } - String s = schematics.get(num); - - File schematic = new File(plugin.getDataFolder() + "/schematics/" + plugin.getConfig().getString(ex + s + ".Schematic")); - if(!schematic.exists()){ - plugin.getLogger().severe("Error: The schematic file for " + s + " does not exist!"); - plugin.getLogger().severe("Please put that file in the Schematics folder!"); - plugin.getLogger().severe("For assistance please contact Ryandw11 on spigot."); - this.cancel(); - CustomStructures.enabled = false; - } - num += 1; - } - -} diff --git a/src/com/ryandw11/structure/utils/HandleY.java b/src/com/ryandw11/structure/utils/HandleY.java deleted file mode 100644 index 3ff5c81..0000000 --- a/src/com/ryandw11/structure/utils/HandleY.java +++ /dev/null @@ -1,107 +0,0 @@ -package com.ryandw11.structure.utils; - -import java.util.Random; -import java.util.concurrent.ThreadLocalRandom; - -import org.bukkit.configuration.ConfigurationSection; - -public class HandleY { - - /** - * Calculate the y value for a structure. - * @param cs The configurationsection of the schematic - * @param height The current height at that area. - * @return the height. (Will never be null) - */ - public static int calculateY(ConfigurationSection cs, int height) { - String value = cs.getString("SpawnY"); - - if(value.equalsIgnoreCase("top")) { - return height; - } - - if(value.contains("[")) { - //If +[num-num] - if(value.startsWith("+")) { - String v = value.replace("[", "").replace("]", "").replace("+", ""); - String[] out = v.split("-"); - try { - int num1 = Integer.parseInt(out[0]); - int num2 = Integer.parseInt(out[1]); - - Random r = new Random(); - - int a = r.nextInt(num2) + (num1 + 1); - return height + a; - - }catch(NumberFormatException | ArrayIndexOutOfBoundsException ex) { - return height; - } - } - // if -[num-num] - else if(value.startsWith("-")) { - String v = value.replace("[", "").replace("]", "").replace("-", ""); - String[] out = v.split("-"); - try { - int num1 = Integer.parseInt(out[0]); - int num2 = Integer.parseInt(out[1]); - - Random r = new Random(); - - int a = r.nextInt(num2) + (num1 + 1); - return height - a; - - }catch(NumberFormatException | ArrayIndexOutOfBoundsException ex) { - return height; - } - } - // if just [num-num] - else { - String v = value.replace("[", "").replace("]", ""); - String[] out = v.split("-"); - try { - int num1 = Integer.parseInt(out[0]); - int num2 = Integer.parseInt(out[1]); - - //Random r = new Random(); - //return r.nextInt(num2 + 1) + num1; - - return ThreadLocalRandom.current().nextInt(num1, num2 + 1); - - }catch(NumberFormatException | ArrayIndexOutOfBoundsException ex) { - return height; - } - } - } - // if +num - else if(value.startsWith("+")) { - String v = value.replace("+", ""); - try { - int num = Integer.parseInt(v); - return height + num; - }catch(NumberFormatException ex) { - return height; - } - } - // if -num - else if(value.startsWith("-")) { - String v = value.replace("-", ""); - - try { - int num = Integer.parseInt(v); - return height - num; - }catch(NumberFormatException ex) { - return height; - } - } - // if just num - else { - try { - return Integer.parseInt(value); - } catch(NumberFormatException ex) { - return height; - } - } - } - -} diff --git a/src/com/ryandw11/structure/utils/RandomCollection.java b/src/com/ryandw11/structure/utils/RandomCollection.java index 3a40ceb..7bce6cf 100644 --- a/src/com/ryandw11/structure/utils/RandomCollection.java +++ b/src/com/ryandw11/structure/utils/RandomCollection.java @@ -1,11 +1,12 @@ package com.ryandw11.structure.utils; +import java.util.Map; import java.util.NavigableMap; import java.util.Random; import java.util.TreeMap; public class RandomCollection { - private final NavigableMap map = new TreeMap(); + private final NavigableMap map = new TreeMap<>(); private final Random random; private double total = 0; @@ -29,4 +30,12 @@ public E next() { double value = random.nextDouble() * total; return map.higherEntry(value).getValue(); } + + public boolean isEmpty(){ + return map.isEmpty(); + } + + public Map getMap(){ + return map; + } } \ No newline at end of file diff --git a/src/com/ryandw11/structure/utils/StructurePicker.java b/src/com/ryandw11/structure/utils/StructurePicker.java index 1c6c143..a9daca8 100644 --- a/src/com/ryandw11/structure/utils/StructurePicker.java +++ b/src/com/ryandw11/structure/utils/StructurePicker.java @@ -2,18 +2,18 @@ import java.io.IOException; import java.util.ArrayList; -import java.util.List; -import java.util.Random; +import com.ryandw11.structure.structure.Structure; +import com.ryandw11.structure.structure.StructureHandler; +import com.ryandw11.structure.structure.properties.BlockLevelLimit; +import com.ryandw11.structure.structure.properties.StructureYSpawning; import org.bukkit.Chunk; import org.bukkit.Material; import org.bukkit.block.Block; -import org.bukkit.configuration.ConfigurationSection; import org.bukkit.scheduler.BukkitRunnable; import com.ryandw11.structure.CustomStructures; import com.ryandw11.structure.SchematicHandeler; -import com.ryandw11.structure.api.CustomStructuresAPI; import com.sk89q.worldedit.WorldEditException; /** @@ -22,141 +22,125 @@ *

* The server will still lag a bit thanks to the nature of 1.14. *

- * - * @author Ryandw11 * + * @author Ryandw11 */ public class StructurePicker extends BukkitRunnable { - private CustomStructures plugin; - private CustomStructuresAPI api; - - private Random r; - private int count; - private String currentSchem; - private int numberOfSchem; - - private Block bl; - private Chunk ch; - - public StructurePicker(Block bl, Chunk ch) { - this.plugin = CustomStructures.plugin; - r = new Random(); - count = 0; - api = new CustomStructuresAPI(); - numberOfSchem = api.getNumberOfStructures(); - this.bl = bl; - this.ch = ch; - } - - @Override - public void run() { - if (count >= numberOfSchem) { - this.cancel(); - return; - } - - currentSchem = plugin.structures.get(count); - - // Calculate the chance. - int num = r.nextInt(plugin.getConfig().getInt("Schematics." + currentSchem + ".Chance.OutOf") - 1) + 1; - if (num <= plugin.getConfig().getInt("Schematics." + currentSchem + ".Chance.Number")) { - if (!plugin.getConfig().getBoolean("Schematics." + currentSchem + ".AllWorlds")) { // Checking to see if the - // world is correct - ArrayList worlds = (ArrayList) plugin.getConfig() - .getStringList("Schematics." + currentSchem + ".AllowedWorlds"); - if (!worlds.contains(bl.getWorld().getName())) - return; - } - - - - ConfigurationSection cs = plugin.getConfig().getConfigurationSection("Schematics." + currentSchem); - - // Allows the structure to spawn based on the ocean floor. (If the floor is not found than it just returns with the top of the water). - if(cs.getBoolean("Ocean_Properties.useOceanFloor")) { - if(bl.getType() == Material.WATER) { - for(int i = bl.getY(); i >= 4; i--) { - if(ch.getBlock(0, i, 0).getType() != Material.WATER) { - bl = ch.getBlock(0, i, 0); - break; - } - } - } - } - - // Allows the structures to no longer spawn on plant life. - if(cs.getBoolean("ignorePlants") && CSConstants.leafBlocks.contains(bl.getType())) { - for(int i = bl.getY(); i <= 4; i--) { - if(!CSConstants.leafBlocks.contains(ch.getBlock(0, i, 0).getType())) { - bl = ch.getBlock(0, i, 0); - break; - } - } - } - - //If it can spawn in a biome. - if (!plugin.getConfig().getString("Schematics." + currentSchem + ".Biome").equalsIgnoreCase("all")) {// Checking - // biome - if (!getBiomes(plugin.getConfig().getString("Schematics." + currentSchem + ".Biome").toLowerCase()) - .contains(bl.getBiome().toString().toLowerCase())) - return; - } - // calculate spawny - if(cs.contains("SpawnY")) { - bl = ch.getBlock(0, HandleY.calculateY(cs, bl.getY()), 0); - } - if(cs.contains("whitelistSpawnBlocks")) { - List materials = new ArrayList<>(); - for(String s : cs.getStringList("whitelistSpawnBlocks")) { - try { - materials.add(Material.valueOf(s.toUpperCase())); - } - catch (Exception e) { - continue; - } - } - - // If the material is not whitelisted. - if(!materials.contains(bl.getType())) { - return; - } - } - // If it can spawn in a liquid - if (!cs.getBoolean("Ocean_Properties.spawnInLiquid")) { - if (bl.getType() == Material.WATER || bl.getType() == Material.LAVA) - return; - } - // Now to finally paste the schematic - SchematicHandeler sh = new SchematicHandeler(); - try { - RandomCollection lootTables = new RandomCollection<>(); - ConfigurationSection lootTablesCS = cs.getConfigurationSection("LootTables"); - if (lootTablesCS != null) { - for (String name : lootTablesCS.getKeys(true)) { - int weight = cs.getInt("LootTables." + name); - lootTables.add(weight, name); - } - } - // Line to actualy paste the schematic. - sh.schemHandle(bl.getLocation(), - plugin.getConfig().getString("Schematics." + currentSchem + ".Schematic"), - plugin.getConfig().getBoolean("Schematics." + currentSchem + ".PlaceAir"), lootTables, cs); - } catch (IOException | WorldEditException e) { - e.printStackTrace(); - } - this.cancel();// return after pasting - } - - count++; - } - - protected ArrayList getBiomes(String s) { - String[] biomes = s.split(","); - ArrayList output = new ArrayList(); - for (String b : biomes) - output.add(b); - return output; - } + private CustomStructures plugin; + + private int currentStructure; + private StructureHandler structureHandler; + + private Block bl; + private Chunk ch; + + public StructurePicker(Block bl, Chunk ch, CustomStructures plugin) { + this.plugin = plugin; + currentStructure = -1; + this.bl = bl; + this.ch = ch; + this.structureHandler = plugin.getStructureHandler(); + } + + @Override + public void run() { + try{ + currentStructure++; + if (currentStructure >= structureHandler.getStructures().size()) { + this.cancel(); + return; + } + + Structure structure = structureHandler.getStructure(currentStructure); + StructureYSpawning structureSpawnSettings = structure.getStructureLocation().getSpawnSettings(); + + // Calculate the chance. + if (!structure.canSpawn(bl, ch)) + return; + + // Allows the structure to spawn based on the ocean floor. (If the floor is not found than it just returns with the top of the water). + if (structureSpawnSettings.isOceanFloor()) { + if (bl.getType() == Material.WATER) { + for (int i = bl.getY(); i >= 4; i--) { + if (ch.getBlock(0, i, 0).getType() != Material.WATER) { + bl = ch.getBlock(0, i, 0); + break; + } + } + } + } + + // Allows the structures to no longer spawn on plant life. + if (structure.getStructureProperties().isIgnoringPlants() && CSConstants.plantBlocks.contains(bl.getType())) { + for (int i = bl.getY(); i <= 4; i--) { + if (!CSConstants.plantBlocks.contains(ch.getBlock(0, i, 0).getType())) { + bl = ch.getBlock(0, i, 0); + break; + } + } + } + + // calculate spawny + if (!structureSpawnSettings.isOceanFloor()) { + bl = ch.getBlock(0, structureSpawnSettings.getHeight(bl.getY()), 0); + } + + if (!structure.getStructureLimitations().hasBlock(bl)) + return; + + // If it can spawn in water + if (!structure.getStructureProperties().canSpawnInWater()) { + if (bl.getType() == Material.WATER) return; + } + + // If the structure can spawn in lava + if (!structure.getStructureProperties().canSpawnInLavaLakes()) { + if (bl.getType() == Material.LAVA) return; + } + + // If the structure can follows block level limit. + if(structure.getStructureLimitations().getBlockLevelLimit().isEnabled()){ + BlockLevelLimit limit = structure.getStructureLimitations().getBlockLevelLimit(); + for(int x = limit.getX1(); x <= limit.getX2(); x++){ + for(int z = limit.getZ1(); z <= limit.getZ2(); z++){ + Block b = ch.getWorld().getBlockAt(x, bl.getY(), z); + if(b.getType() == Material.AIR) return; + } + } + } + + // Now to finally paste the schematic + SchematicHandeler sh = new SchematicHandeler(); + plugin.getServer().getScheduler().scheduleSyncDelayedTask(plugin, () -> { + try { + sh.schemHandle(bl.getLocation(), + structure.getSchematic(), + structure.getStructureProperties().canPlaceAir(), + structure.getLootTables(), + structure); + } catch (IOException | WorldEditException e) { + e.printStackTrace(); + } + }); + + this.cancel();// return after pasting + }catch(Exception ex){ + this.cancel(); + plugin.getLogger().severe("An error was encountered during the schematic pasting section."); + plugin.getLogger().severe("The task was stopped for the safety of your server!"); + plugin.getLogger().severe("For more information enable debug mode."); + if(plugin.isDebug()) + ex.printStackTrace(); + } + } + + protected ArrayList getBiomes(String s) { + String[] biomes = s.split(","); + ArrayList output = new ArrayList(); + for (String b : biomes) + output.add(b); + return output; + } } diff --git a/src/config.yml b/src/config.yml new file mode 100644 index 0000000..dda3cae --- /dev/null +++ b/src/config.yml @@ -0,0 +1,19 @@ +configversion: 5 +#======================================== +# Custom Structures +#======================================== +#Wiki: https://github.com/ryandw11/CustomStructures/wiki + +#Log extra information in console when generating structures +debug: false + +#bstats is where data is sent. https://bstats.org/plugin/bukkit/CustomStructures . +#Here is some more information on it like what it collects and how to disable it all togeather and not just for this plugin. +# https://bstats.org/getting-started +bstats: true + +#Allow Plugins to delete structures at will. +allowDeletion: true + +Structures: + - demo \ No newline at end of file diff --git a/lootTables/lootTable.yml b/src/lootTables/lootTable.yml similarity index 100% rename from lootTables/lootTable.yml rename to src/lootTables/lootTable.yml diff --git a/plugin.yml b/src/plugin.yml similarity index 95% rename from plugin.yml rename to src/plugin.yml index d5fb0aa..23346c7 100644 --- a/plugin.yml +++ b/src/plugin.yml @@ -1,5 +1,5 @@ name: CustomStructures -version: 1.4.3.2 +version: 1.5 main: com.ryandw11.structure.CustomStructures author: Ryandw11 description: A plugin which allows you to spawn in custom structures! diff --git a/src/schematics/demo.schem b/src/schematics/demo.schem new file mode 100644 index 0000000..e9b64dc Binary files /dev/null and b/src/schematics/demo.schem differ diff --git a/src/structures/demo.yml b/src/structures/demo.yml new file mode 100644 index 0000000..c3cafcd --- /dev/null +++ b/src/structures/demo.yml @@ -0,0 +1,45 @@ +schematic: 'demo.schem' + +Chance: + Number: 1 + OutOf: 500 + +StructureLocation: + Worlds: + - world + SpawnY: top + Biome: 'all' + +StructureProperties: + PlaceAir: true + randomRotation: false + ignorePlants: true + spawnInWater: true + spawnInLavaLakes: true + +StructureLimitations: + whitelistSpawnBlocks: + - GRASS_BLOCK + - Dirt + - Stone + - air + + # This could cause issue with lag depending on how large the search area is. + BlockLevelLimit: + mode: flat + cornerOne: + x: 5 + z: 5 + cornerTwo: + x: -5 + z: -5 + + # This could cause issues with lag depending on how many blocks you have + # to replace. + replacement_blocks: + 'STONE': 'AIR' + +#List of lootTables for this Schematic, name and weight +#Weight determines how often it will be chosen out of all the entries in the list. +LootTables: + lootTable: 5 \ No newline at end of file