diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4788b4b --- /dev/null +++ b/.gitignore @@ -0,0 +1,113 @@ +# User-specific stuff +.idea/ + +*.iml +*.ipr +*.iws + +# IntelliJ +out/ + +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +target/ + +pom.xml.tag +pom.xml.releaseBackup +pom.xml.versionsBackup +pom.xml.next + +release.properties +dependency-reduced-pom.xml +buildNumber.properties +.mvn/timing.properties +.mvn/wrapper/maven-wrapper.jar +.flattened-pom.xml + +# Common working directory +run/ diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..c232b01 --- /dev/null +++ b/pom.xml @@ -0,0 +1,117 @@ + + + 4.0.0 + + net.momirealms + CustomCrops + 1.0-SNAPSHOT + jar + + CustomCrops + + + 1.8 + UTF-8 + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + + ${java.version} + ${java.version} + + + + org.apache.maven.plugins + maven-shade-plugin + 3.2.4 + + + package + + shade + + + false + + + + + + + + src/main/resources + true + + + + + + + dmulloy2-repo + https://repo.dmulloy2.net/repository/public/ + + + placeholderapi + https://repo.extendedclip.com/content/repositories/placeholderapi/ + + + sonatype-oss-snapshots1 + https://s01.oss.sonatype.org/content/repositories/snapshots/ + + + papermc-repo + https://papermc.io/repo/repository/maven-public/ + + + jitpack-repo + https://jitpack.io + + + sonatype + https://oss.sonatype.org/content/groups/public/ + + + + + + com.comphenix.protocol + ProtocolLib + 4.8.0 + + + me.clip + placeholderapi + 2.11.1 + provided + + + net.kyori + adventure-text-minimessage + 4.10.1 + + + net.kyori + adventure-platform-bukkit + 4.1.0 + + + io.papermc.paper + paper-api + 1.18.1-R0.1-SNAPSHOT + provided + + + com.github.LoneDev6 + api-itemsadder + 3.1.0b + provided + + + diff --git a/src/main/java/net/momirealms/customcrops/CommandHandler.java b/src/main/java/net/momirealms/customcrops/CommandHandler.java new file mode 100644 index 0000000..b4a7ffc --- /dev/null +++ b/src/main/java/net/momirealms/customcrops/CommandHandler.java @@ -0,0 +1,96 @@ +package net.momirealms.customcrops; + +import net.momirealms.customcrops.DataManager.BackUp; +import net.momirealms.customcrops.DataManager.CropManager; +import net.momirealms.customcrops.DataManager.SprinklerManager; +import org.bukkit.Bukkit; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.entity.Player; + +import javax.annotation.ParametersAreNonnullByDefault; +import java.util.Objects; + +public class CommandHandler implements CommandExecutor { + + + @Override + @ParametersAreNonnullByDefault + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + + if(sender instanceof Player && !sender.isOp()){ + return false; + } + + FileConfiguration config = CustomCrops.instance.getConfig(); + + if(args[0].equalsIgnoreCase("reload")){ + CustomCrops.loadConfig(); + if(sender instanceof Player){ + MessageManager.playerMessage(config.getString("messages.prefix") + config.getString("messages.reload"), (Player) sender); + }else { + MessageManager.consoleMessage(config.getString("messages.prefix") + config.getString("messages.reload"), Bukkit.getConsoleSender()); + } + } + + if(args[0].equalsIgnoreCase("setseason")){ + if(config.getBoolean("enable-season")){ + config.set("current-season", args[1]); + if(sender instanceof Player){ + MessageManager.playerMessage(config.getString("messages.prefix") + Objects.requireNonNull(config.getString("messages.season-set")).replace("{Season}",args[1]) + .replace("spring", Objects.requireNonNull(config.getString("messages.spring"))) + .replace("summer", Objects.requireNonNull(config.getString("messages.summer"))) + .replace("autumn", Objects.requireNonNull(config.getString("messages.autumn"))) + .replace("winter", Objects.requireNonNull(config.getString("messages.winter"))), (Player) sender); + }else { + MessageManager.consoleMessage(config.getString("messages.prefix") + Objects.requireNonNull(config.getString("messages.season-set")).replace("{Season}",args[1]) + .replace("spring", Objects.requireNonNull(config.getString("messages.spring"))) + .replace("summer", Objects.requireNonNull(config.getString("messages.summer"))) + .replace("autumn", Objects.requireNonNull(config.getString("messages.autumn"))) + .replace("winter", Objects.requireNonNull(config.getString("messages.winter"))), Bukkit.getConsoleSender()); + } + CustomCrops.instance.saveConfig(); + }else{ + if(sender instanceof Player){ + MessageManager.playerMessage(config.getString("messages.prefix") + config.getString("messages.season-disabled"), (Player) sender); + }else { + MessageManager.consoleMessage(config.getString("messages.prefix") + config.getString("messages.season-disabled"), Bukkit.getConsoleSender()); + } + } + } + + if(args[0].equalsIgnoreCase("forcesave")){ + CropManager.saveData(); + SprinklerManager.saveData(); + if(sender instanceof Player){ + MessageManager.playerMessage(config.getString("messages.prefix") + config.getString("messages.force-save"), (Player) sender); + }else { + MessageManager.consoleMessage(config.getString("messages.prefix") + config.getString("messages.force-save"), Bukkit.getConsoleSender()); + } + } + + if(args[0].equalsIgnoreCase("cleancache")){ + Bukkit.getScheduler().runTaskAsynchronously(CustomCrops.instance,()->{ + CropManager.cleanLoadedCache(); + SprinklerManager.cleanCache(); + }); + if(sender instanceof Player){ + MessageManager.playerMessage(config.getString("messages.prefix") + config.getString("messages.clean-cache"), (Player) sender); + }else { + MessageManager.consoleMessage(config.getString("messages.prefix") + config.getString("messages.clean-cache"), Bukkit.getConsoleSender()); + } + } + + if(args[0].equalsIgnoreCase("backup")){ + BackUp.backUpData(); + if(sender instanceof Player){ + MessageManager.playerMessage(config.getString("messages.prefix") + config.getString("messages.backup"), (Player) sender); + }else { + MessageManager.consoleMessage(config.getString("messages.prefix") + config.getString("messages.backup"), Bukkit.getConsoleSender()); + } + } + return false; + } +} diff --git a/src/main/java/net/momirealms/customcrops/CommandTabComplete.java b/src/main/java/net/momirealms/customcrops/CommandTabComplete.java new file mode 100644 index 0000000..ccd07ba --- /dev/null +++ b/src/main/java/net/momirealms/customcrops/CommandTabComplete.java @@ -0,0 +1,24 @@ +package net.momirealms.customcrops; + +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.command.TabCompleter; +import org.jetbrains.annotations.Nullable; + +import javax.annotation.ParametersAreNonnullByDefault; +import java.util.Arrays; +import java.util.List; + +public class CommandTabComplete implements TabCompleter { + @Override + @ParametersAreNonnullByDefault + public @Nullable List onTabComplete(CommandSender sender, Command command, String alias, String[] args) { + if (args.length == 1) { + return Arrays.asList("backup" , "cleancache", "forcesave", "reload", "setseason"); + } + if(args[0].equalsIgnoreCase("setseason")){ + return Arrays.asList("spring","summer","autumn","winter"); + } + return null; + } +} diff --git a/src/main/java/net/momirealms/customcrops/Crops/CropTimer.java b/src/main/java/net/momirealms/customcrops/Crops/CropTimer.java new file mode 100644 index 0000000..a18db56 --- /dev/null +++ b/src/main/java/net/momirealms/customcrops/Crops/CropTimer.java @@ -0,0 +1,25 @@ +package net.momirealms.customcrops.Crops; + +import net.momirealms.customcrops.CustomCrops; +import org.bukkit.Bukkit; +import org.bukkit.scheduler.BukkitTask; + +public class CropTimer { + + int taskID; + + public static void stopTimer(int ID) { + Bukkit.getScheduler().cancelTask(ID); + Bukkit.getServer().getScheduler().cancelTask(ID); + } + + public CropTimer() { + TimeCheck tc = new TimeCheck(); + BukkitTask task = tc.runTaskTimerAsynchronously(CustomCrops.instance, 1,1); + this.taskID = task.getTaskId(); + } + + public int getTaskID() { + return this.taskID; + } +} diff --git a/src/main/java/net/momirealms/customcrops/Crops/TimeCheck.java b/src/main/java/net/momirealms/customcrops/Crops/TimeCheck.java new file mode 100644 index 0000000..bb392da --- /dev/null +++ b/src/main/java/net/momirealms/customcrops/Crops/TimeCheck.java @@ -0,0 +1,167 @@ +package net.momirealms.customcrops.Crops; + +import com.comphenix.protocol.PacketType; +import com.comphenix.protocol.events.PacketContainer; +import dev.lone.itemsadder.api.CustomBlock; +import net.momirealms.customcrops.CustomCrops; +import net.momirealms.customcrops.DataManager.CropManager; +import net.momirealms.customcrops.DataManager.SprinklerManager; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.scheduler.BukkitRunnable; + +import java.io.File; +import java.util.Objects; + +public class TimeCheck extends BukkitRunnable { + + FileConfiguration config = CustomCrops.instance.getConfig(); + + @Override + public void run() { + long time = Bukkit.getWorld("world").getTime(); + if(time == 23500){ + CropManager.cleanLoadedCache(); + } + if(time == 23700){ + CropManager.saveData(); + } + if(time == 23900){ + File file = new File(CustomCrops.instance.getDataFolder(), "crop-data.yml"); + FileConfiguration data; + data = YamlConfiguration.loadConfiguration(file); + boolean enable_season = config.getBoolean("enable-season"); + boolean enable_greenhouse = config.getBoolean("config.enable-greenhouse"); + int range = config.getInt("config.greenhouse-range"); + String glass = config.getString("config.greenhouse-glass"); + config.getStringList("config.whitelist-worlds").forEach(worldName -> CropManager.getCrops(Objects.requireNonNull(Bukkit.getWorld(worldName))).forEach(seedLocation -> { + World world = Bukkit.getWorld(worldName); + Location potLocation = seedLocation.clone().subtract(0,1,0); + String[] seasons = Objects.requireNonNull(data.getString(worldName + "." + seedLocation.getBlockX() + "," + seedLocation.getBlockY() + "," + seedLocation.getBlockZ())).split(","); + if (CustomBlock.byAlreadyPlaced(world.getBlockAt(potLocation)) != null && CustomBlock.byAlreadyPlaced(world.getBlockAt(seedLocation)) != null){ + if (CustomBlock.byAlreadyPlaced((world.getBlockAt(potLocation))).getNamespacedID().equalsIgnoreCase(config.getString("config.watered-pot")) && CustomBlock.byAlreadyPlaced(world.getBlockAt(seedLocation)).getNamespacedID().contains("stage")){ + if (CustomBlock.byAlreadyPlaced(world.getBlockAt(seedLocation)).getNamespacedID().equalsIgnoreCase(config.getString("config.dead-crop"))){ + return; + } + String[] cropNameList = CustomBlock.byAlreadyPlaced(world.getBlockAt(seedLocation)).getNamespacedID().split("_"); + Label_out: + if(enable_season){ + if(enable_greenhouse){ + for(int i = 1; i <= range; i++){ + Location tempLocation = seedLocation.clone().add(0,i,0); + if (CustomBlock.byAlreadyPlaced(world.getBlockAt(tempLocation)) != null){ + if(CustomBlock.byAlreadyPlaced(world.getBlockAt(tempLocation)).getNamespacedID().equalsIgnoreCase(glass)){ + break Label_out; + } + } + } + } + boolean wrongSeason = true; + for(String season : seasons){ + if(Objects.equals(season, config.getString("current-season"))){ + wrongSeason = false; + } + } + if(wrongSeason){ + Bukkit.getScheduler().callSyncMethod(CustomCrops.instance, () -> { + CustomBlock.remove(seedLocation); + CustomBlock.place(config.getString("config.dead-crop"),seedLocation); + return null; + }); + return; + } + } + int nextStage = Integer.parseInt(cropNameList[2]) + 1; + if (CustomBlock.getInstance(cropNameList[0] + "_" +cropNameList[1] +"_" + nextStage) != null) { + Bukkit.getScheduler().callSyncMethod(CustomCrops.instance, () ->{ + CustomBlock.remove(potLocation); + CustomBlock.place(config.getString("config.pot"),potLocation); + if(Math.random()< config.getDouble("config.grow-success-chance")){ + CustomBlock.remove(seedLocation); + CustomBlock.place(cropNameList[0] + "_" +cropNameList[1] +"_" + nextStage,seedLocation); + } + return null; + }); + } + }else if(CustomBlock.byAlreadyPlaced((world.getBlockAt(potLocation))).getNamespacedID().equalsIgnoreCase(config.getString("config.pot")) && CustomBlock.byAlreadyPlaced(world.getBlockAt(seedLocation)).getNamespacedID().contains("stage")){ + if (CustomBlock.byAlreadyPlaced(world.getBlockAt(seedLocation)).getNamespacedID().equalsIgnoreCase(config.getString("config.dead-crop"))){ + return; + } + if(enable_season) { + if(enable_greenhouse){ + for(int i = 1; i <= range; i++){ + Location tempLocation = seedLocation.clone().add(0,i,0); + if (CustomBlock.byAlreadyPlaced(world.getBlockAt(tempLocation)) != null){ + if(CustomBlock.byAlreadyPlaced(world.getBlockAt(tempLocation)).getNamespacedID().equalsIgnoreCase(config.getString("config.greenhouse-glass"))){ + return; + } + } + } + } + boolean wrongSeason = true; + for (String season : seasons) { + if (Objects.equals(season, config.getString("current-season"))) { + wrongSeason = false; + } + } + if (wrongSeason) { + Bukkit.getScheduler().callSyncMethod(CustomCrops.instance, () -> { + CustomBlock.remove(seedLocation); + CustomBlock.place(config.getString("config.dead-crop"), seedLocation); + return null; + }); + } + } + } + } + })); + } + if (time == 100){ + SprinklerManager.cleanCache(); + } + if (time == 300){ + SprinklerManager.saveData(); + } + if(time == 500){ + File file = new File(CustomCrops.instance.getDataFolder(), "sprinkler-data.yml"); + FileConfiguration data; + data = YamlConfiguration.loadConfiguration(file); + config.getStringList("config.whitelist-worlds").forEach(worldName -> SprinklerManager.getSprinklers(Objects.requireNonNull(Bukkit.getWorld(worldName))).forEach(location -> { + World world = Bukkit.getWorld(worldName); + String type = Objects.requireNonNull(data.getString(worldName + "." + location.getBlockX() + "," + location.getBlockY() + "," + location.getBlockZ())); + if(type.equals("s1")){ + for(int i = -1; i <= 1;i++){ + for (int j = -1; j <= 1; j++){ + Location tempLoc = location.clone().add(i,-1,j); + waterPot(tempLoc, world); + } + } + }else if(type.equals("s2")){ + for(int i = -2; i <= 2;i++){ + for (int j = -2; j <= 2; j++){ + Location tempLoc = location.clone().add(i,-1,j); + waterPot(tempLoc, world); + } + } + } + })); + } + } + private void waterPot(Location tempLoc, World world) { + if(CustomBlock.byAlreadyPlaced(world.getBlockAt(tempLoc)) != null){ + if(CustomBlock.byAlreadyPlaced(world.getBlockAt(tempLoc)).getNamespacedID().equalsIgnoreCase(config.getString("config.pot"))){ + + PacketContainer fakeWater = new PacketContainer(PacketType.Play.Server.ENTITY_HEAD_ROTATION); + + Bukkit.getScheduler().callSyncMethod(CustomCrops.instance,()->{ + CustomBlock.remove(tempLoc); + CustomBlock.place(config.getString("config.watered-pot"), tempLoc); + return null; + }); + } + } + } +} diff --git a/src/main/java/net/momirealms/customcrops/CustomCrops.java b/src/main/java/net/momirealms/customcrops/CustomCrops.java new file mode 100644 index 0000000..c61e821 --- /dev/null +++ b/src/main/java/net/momirealms/customcrops/CustomCrops.java @@ -0,0 +1,110 @@ +package net.momirealms.customcrops; + +import net.kyori.adventure.platform.bukkit.BukkitAudiences; +import net.momirealms.customcrops.Crops.CropTimer; +import net.momirealms.customcrops.DataManager.BackUp; +import net.momirealms.customcrops.DataManager.CropManager; +import net.momirealms.customcrops.DataManager.SprinklerManager; +import net.momirealms.customcrops.listener.BreakCustomBlock; +import net.momirealms.customcrops.listener.BreakFurniture; +import net.momirealms.customcrops.listener.RightClickBlock; +import net.momirealms.customcrops.listener.RightClickCustomBlock; +import org.bukkit.Bukkit; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.plugin.java.JavaPlugin; +import org.checkerframework.checker.nullness.qual.NonNull; + +import java.io.File; +import java.io.IOException; +import java.util.List; +import java.util.Objects; + +public final class CustomCrops extends JavaPlugin { + + public static JavaPlugin instance; + public static ProtocolManager manager; + public static CropTimer timer; + public static CropManager cropManager; + public static SprinklerManager sprinklerManager; + private BukkitAudiences adventure; + + @Override + public void onEnable() { + instance = this; + saveDefaultConfig(); + + Objects.requireNonNull(Bukkit.getPluginCommand("customcrops")).setExecutor(new CommandHandler()); + Objects.requireNonNull(Bukkit.getPluginCommand("customcrops")).setTabCompleter(new CommandTabComplete()); + Bukkit.getPluginManager().registerEvents(new RightClickCustomBlock(),this); + Bukkit.getPluginManager().registerEvents(new BreakCustomBlock(),this); + Bukkit.getPluginManager().registerEvents(new RightClickBlock(),this); + Bukkit.getPluginManager().registerEvents(new BreakFurniture(),this); + + startTimer(); + + File crop_file = new File(CustomCrops.instance.getDataFolder(), "crop-data.yml"); + File sprinkler_file = new File(CustomCrops.instance.getDataFolder(), "sprinkler-data.yml"); + if(!crop_file.exists()){ + try { + crop_file.createNewFile(); + } catch (IOException e) { + e.printStackTrace(); + } + } + if(!sprinkler_file.exists()){ + try { + sprinkler_file.createNewFile(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + FileConfiguration crop_data; + FileConfiguration sprinkler_data; + crop_data = YamlConfiguration.loadConfiguration(crop_file); + sprinkler_data = YamlConfiguration.loadConfiguration(sprinkler_file); + CustomCrops.cropManager = new CropManager(crop_data); + CustomCrops.sprinklerManager = new SprinklerManager(sprinkler_data); + + if(Bukkit.getPluginManager().getPlugin("PlaceholderAPI") != null){ + new Placeholders(this).register(); + MessageManager.consoleMessage("[CustomCorps] 检测到PlaceHolderAPI 已启用季节变量!",Bukkit.getConsoleSender()); + } + + MessageManager.consoleMessage("[CustomCorps] 自定义农作物插件已启用!作者:小默米",Bukkit.getConsoleSender()); + } + + @Override + public void onDisable() { + if (CustomCrops.timer != null) { + CropTimer.stopTimer(CustomCrops.timer.getTaskID()); + } + + CropManager.saveData(); + SprinklerManager.saveData(); + + if(this.adventure != null) { + this.adventure.close(); + this.adventure = null; + } + + BackUp.backUpData(); + MessageManager.consoleMessage("[CustomCorps] 自定义农作物插件已卸载!作者:小默米",Bukkit.getConsoleSender()); + } + + public @NonNull BukkitAudiences adventure() { + if(this.adventure == null) { + throw new IllegalStateException("Tried to access Adventure when the plugin was disabled!"); + } + return this.adventure; + } + + public static void startTimer() { + CustomCrops.timer = new CropTimer(); + } + + public static void loadConfig(){ + instance.reloadConfig(); + } +} diff --git a/src/main/java/net/momirealms/customcrops/DataManager/BackUp.java b/src/main/java/net/momirealms/customcrops/DataManager/BackUp.java new file mode 100644 index 0000000..c1c5d35 --- /dev/null +++ b/src/main/java/net/momirealms/customcrops/DataManager/BackUp.java @@ -0,0 +1,48 @@ +package net.momirealms.customcrops.DataManager; + +import net.momirealms.customcrops.CustomCrops; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.text.SimpleDateFormat; +import java.util.Date; + +public class BackUp { + public static void backUpData(){ + + Date date = new Date(); + SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss"); + + File crop_data = new File(CustomCrops.instance.getDataFolder(), "crop-data.yml"); + File cropBackUp = new File(CustomCrops.instance.getDataFolder(), "backups/"+ format.format(date) + "/" + "crop-data.yml"); + File sprinkler_data = new File(CustomCrops.instance.getDataFolder(), "sprinkler-data.yml"); + File sprinklerBackUp = new File(CustomCrops.instance.getDataFolder(), "backups/"+ format.format(date) + "/" + "sprinkler-data.yml"); + + try { + BackUp.backUp(crop_data,cropBackUp); + BackUp.backUp(sprinkler_data,sprinklerBackUp); + } catch (IOException e) { + e.printStackTrace(); + } + } + + private static void backUp(File file_from, File file_to) throws IOException { + if(!file_to.exists()){ + file_to.getParentFile().mkdirs(); + } + FileInputStream fis = new FileInputStream(file_from); + if(!file_to.exists()){ + file_to.createNewFile(); + } + FileOutputStream fos = new FileOutputStream(file_to); + byte[] b = new byte[1024]; + int len; + while ((len = fis.read(b))!= -1){ + fos.write(b,0,len); + } + fos.close(); + fis.close(); + } +} diff --git a/src/main/java/net/momirealms/customcrops/DataManager/CropManager.java b/src/main/java/net/momirealms/customcrops/DataManager/CropManager.java new file mode 100644 index 0000000..7b7e94e --- /dev/null +++ b/src/main/java/net/momirealms/customcrops/DataManager/CropManager.java @@ -0,0 +1,119 @@ +package net.momirealms.customcrops.DataManager; + +import net.momirealms.customcrops.CustomCrops; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; + +import java.io.File; +import java.io.IOException; +import java.util.*; + +public class CropManager { + + public static HashMap instances; + + public CropManager(FileConfiguration data) { + FileConfiguration config = CustomCrops.instance.getConfig(); + File file = new File(CustomCrops.instance.getDataFolder(), "crop-data.yml"); + data = YamlConfiguration.loadConfiguration(file); + try { + for (String world : config.getStringList("config.whitelist-worlds")) { + CropManager.instances = new HashMap(); + if(data.getConfigurationSection(world) != null){ + for (String coordinate : data.getConfigurationSection(world).getKeys(false)) { + Location tempLocation = new Location(Bukkit.getWorld(world), (double)Integer.parseInt(coordinate.split(",")[0]), (double)Integer.parseInt(coordinate.split(",")[1]), (double)Integer.parseInt(coordinate.split(",")[2])); + String season = data.getString(world + "." + coordinate); + CropManager.instances.put(tempLocation, season); + } + } + } + } + catch (Exception e) { + CropManager.instances = new HashMap(); + e.printStackTrace(); + } + saveData(); + } + + public static List getCrops(World world){ + FileConfiguration config = CustomCrops.instance.getConfig(); + File file = new File(CustomCrops.instance.getDataFolder(), "crop-data.yml"); + FileConfiguration data; + data = YamlConfiguration.loadConfiguration(file); + List locations = new ArrayList(); + if (config.getStringList("config.whitelist-worlds").contains(world.getName())){ + if(data.contains(world.getName())){ + data.getConfigurationSection(world.getName()).getKeys(false).forEach(key ->{ + String[] string_list = key.split(","); + if (config.getBoolean("config.only-grow-in-loaded-chunks")){ + if (world.isChunkLoaded(Integer.parseInt(string_list[0])/16, Integer.parseInt(string_list[2])/16)){ + locations.add(new Location(world, Double.parseDouble(string_list[0]),Double.parseDouble(string_list[1]),Double.parseDouble(string_list[2]))); + } + }else { + locations.add(new Location(world, Double.parseDouble(string_list[0]),Double.parseDouble(string_list[1]),Double.parseDouble(string_list[2]))); + } + }); + } + } + return locations; + } + + public static void saveData(){ + File file = new File(CustomCrops.instance.getDataFolder(), "crop-data.yml"); + FileConfiguration data; + data = YamlConfiguration.loadConfiguration(file); + if (CropManager.instances != null) { + Set> en = instances.entrySet(); + for(Map.Entry entry : en){ + data.set(entry.getKey().getWorld().getName() + "." + entry.getKey().getBlockX() + "," + entry.getKey().getBlockY()+ ","+entry.getKey().getBlockZ(), entry.getValue()); + } + } + else { + CropManager.instances = new HashMap(); + Bukkit.getConsoleSender().sendMessage("错误"); + } + try { + data.save(file); + } + catch (IOException e) { + e.printStackTrace(); + } + } + //test + public static void putInstance(Location location, String season) { + CropManager.instances.put(location, season); + } + public HashMap getMap() { + return CropManager.instances; + } + public static void cleanLoadedCache() { + FileConfiguration config = CustomCrops.instance.getConfig(); + File file = new File(CustomCrops.instance.getDataFolder(), "crop-data.yml"); + FileConfiguration data; + data = YamlConfiguration.loadConfiguration(file); + config.getStringList("config.whitelist-worlds").forEach(worldName ->{ + if(data.contains(worldName)){ + World world = Bukkit.getWorld(worldName); + data.getConfigurationSection(worldName).getKeys(false).forEach(key ->{ + String[] string_list = key.split(","); + if (world.isChunkLoaded(Integer.parseInt(string_list[0])/16, Integer.parseInt(string_list[2])/16)){ + Location tempLoc = new Location(world,Double.parseDouble(string_list[0]),Double.parseDouble(string_list[1]),Double.parseDouble(string_list[2])); + if(world.getBlockAt(tempLoc).getType() != Material.TRIPWIRE){ + CropManager.instances.remove(tempLoc); + data.set(worldName+"."+string_list[0]+","+string_list[1]+","+string_list[2], null); + } + } + }); + } + }); + try{ + data.save(file); + }catch (IOException e){ + e.printStackTrace(); + } + } +} diff --git a/src/main/java/net/momirealms/customcrops/DataManager/MaxCropsPerChunk.java b/src/main/java/net/momirealms/customcrops/DataManager/MaxCropsPerChunk.java new file mode 100644 index 0000000..5f9b540 --- /dev/null +++ b/src/main/java/net/momirealms/customcrops/DataManager/MaxCropsPerChunk.java @@ -0,0 +1,41 @@ +package net.momirealms.customcrops.DataManager; + +import dev.lone.itemsadder.api.CustomBlock; +import net.momirealms.customcrops.CustomCrops; +import org.bukkit.Location; +import org.bukkit.configuration.file.FileConfiguration; + +public class MaxCropsPerChunk { + + public static boolean maxCropsPerChunk(Location location){ + FileConfiguration config = CustomCrops.instance.getConfig(); + if(!config.getBoolean("config.enable-limit")){ + return false; + } + int maxY = config.getInt("config.height.max"); + int minY = config.getInt("config.height.min"); + int maxAmount = config.getInt("config.max-crops"); + + int n = 1; + + Location chunkLocation = new Location(location.getWorld(),location.getChunk().getX()*16,minY,location.getChunk().getZ()*16); + + Label_out: + for (int i = 0; i < 16; ++i) { + for (int j = 0; j < 16; ++j) { + final Location square = chunkLocation.clone().add((double)i, 0.0, (double)j); + for (int k = minY; k <= maxY; ++k) { + square.add(0.0, 1.0, 0.0); + if(CustomBlock.byAlreadyPlaced(location.getWorld().getBlockAt(square))!= null){ + if (CustomBlock.byAlreadyPlaced(location.getWorld().getBlockAt(square)).getNamespacedID().contains("stage")) { + if (n++ > maxAmount) { + break Label_out; + } + } + } + } + } + } + return n > maxAmount; + } +} diff --git a/src/main/java/net/momirealms/customcrops/DataManager/MaxSprinklersPerChunk.java b/src/main/java/net/momirealms/customcrops/DataManager/MaxSprinklersPerChunk.java new file mode 100644 index 0000000..37db937 --- /dev/null +++ b/src/main/java/net/momirealms/customcrops/DataManager/MaxSprinklersPerChunk.java @@ -0,0 +1,40 @@ +package net.momirealms.customcrops.DataManager; + +import net.momirealms.customcrops.CustomCrops; +import net.momirealms.customcrops.IAFurniture; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.configuration.file.FileConfiguration; + +public class MaxSprinklersPerChunk { + public static boolean maxSprinklersPerChunk(Location location){ + FileConfiguration config = CustomCrops.instance.getConfig(); + if(!config.getBoolean("config.enable-limit")){ + return false; + } + int maxY = config.getInt("config.height.max"); + int minY = config.getInt("config.height.min"); + int maxAmount = config.getInt("config.max-sprinklers"); + + int n = 1; + + Location chunkLocation = new Location(location.getWorld(),location.getChunk().getX()*16,minY,location.getChunk().getZ()*16); + World world = location.getWorld(); + + Label_out: + for (int i = 0; i < 16; ++i) { + for (int j = 0; j < 16; ++j) { + final Location square = chunkLocation.clone().add((double)i, 0.0, (double)j); + for (int k = minY; k <= maxY; ++k) { + square.add(0.0, 1.0, 0.0); + if(IAFurniture.getFromLocation(square.clone().add(0.5,0.5,0.5), world)){ + if (n++ > maxAmount) { + break Label_out; + } + } + } + } + } + return n > maxAmount; + } +} diff --git a/src/main/java/net/momirealms/customcrops/DataManager/SprinklerManager.java b/src/main/java/net/momirealms/customcrops/DataManager/SprinklerManager.java new file mode 100644 index 0000000..01eb35f --- /dev/null +++ b/src/main/java/net/momirealms/customcrops/DataManager/SprinklerManager.java @@ -0,0 +1,113 @@ +package net.momirealms.customcrops.DataManager; + +import net.momirealms.customcrops.CustomCrops; +import net.momirealms.customcrops.IAFurniture; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.configuration.file.YamlConfiguration; + +import java.io.File; +import java.io.IOException; +import java.util.*; + +public class SprinklerManager { + + public static HashMap instances; + + public SprinklerManager(FileConfiguration data) { + FileConfiguration config = CustomCrops.instance.getConfig(); + File file = new File(CustomCrops.instance.getDataFolder(), "sprinkler-data.yml"); + data = YamlConfiguration.loadConfiguration(file); + try { + for (String world : config.getStringList("config.whitelist-worlds")) { + SprinklerManager.instances = new HashMap(); + if(data.getConfigurationSection(world) != null){ + for (String coordinate : data.getConfigurationSection(world).getKeys(false)) { + Location tempLocation = new Location(Bukkit.getWorld(world), Integer.parseInt(coordinate.split(",")[0]), Integer.parseInt(coordinate.split(",")[1]), Integer.parseInt(coordinate.split(",")[2])); + String type = data.getString(world + "." + coordinate); + SprinklerManager.instances.put(tempLocation, type); + } + } + } + } + catch (Exception e) { + SprinklerManager.instances = new HashMap(); + e.printStackTrace(); + } + saveData(); + } + + public static List getSprinklers(World world){ + FileConfiguration config = CustomCrops.instance.getConfig(); + File file = new File(CustomCrops.instance.getDataFolder(), "sprinkler-data.yml"); + FileConfiguration data; + data = YamlConfiguration.loadConfiguration(file); + List locations = new ArrayList(); + if (config.getStringList("config.whitelist-worlds").contains(world.getName())){ + if(data.contains(world.getName())){ + data.getConfigurationSection(world.getName()).getKeys(false).forEach(key ->{ + String[] string_list = key.split(","); + if (config.getBoolean("config.only-grow-in-loaded-chunks")){ + if (world.isChunkLoaded(Integer.parseInt(string_list[0])/16, Integer.parseInt(string_list[2])/16)){ + locations.add(new Location(world, Double.parseDouble(string_list[0]),Double.parseDouble(string_list[1]),Double.parseDouble(string_list[2]))); + } + }else { + locations.add(new Location(world, Double.parseDouble(string_list[0]),Double.parseDouble(string_list[1]),Double.parseDouble(string_list[2]))); + } + }); + } + } + return locations; + } + + public static void saveData(){ + File file = new File(CustomCrops.instance.getDataFolder(), "sprinkler-data.yml"); + FileConfiguration data; + data = YamlConfiguration.loadConfiguration(file); + if (SprinklerManager.instances != null) { + Set> en = instances.entrySet(); + for(Map.Entry entry : en){ + data.set(entry.getKey().getWorld().getName() + "." + entry.getKey().getBlockX() + "," + entry.getKey().getBlockY()+ ","+entry.getKey().getBlockZ(), entry.getValue()); + } + } + else { + SprinklerManager.instances = new HashMap(); + Bukkit.getConsoleSender().sendMessage("错误"); + } + try { + data.save(file); + } + catch (IOException e) { + e.printStackTrace(); + } + } + public static void putInstance(Location location, String type) { + SprinklerManager.instances.put(location, type); + } + public static void cleanCache() { + File file = new File(CustomCrops.instance.getDataFolder(), "sprinkler-data.yml"); + FileConfiguration data; + data = YamlConfiguration.loadConfiguration(file); + Bukkit.getScheduler().callSyncMethod(CustomCrops.instance,()->{ + Set key = new HashSet(instances.keySet()); + try{ + for(Location u_key : key) { + if (!IAFurniture.getFromLocation(u_key.clone().add(0.5,0.5,0.5), u_key.getWorld())){ + SprinklerManager.instances.remove(u_key); + data.set(u_key.getWorld().getName()+"."+u_key.getBlockX()+","+u_key.getBlockY()+","+u_key.getBlockZ(), null); + } + } + }catch (ConcurrentModificationException e){ + e.printStackTrace(); + } + try{ + data.save(file); + }catch (IOException e){ + e.printStackTrace(); + } + return null; + }); + } +} diff --git a/src/main/java/net/momirealms/customcrops/IAFurniture.java b/src/main/java/net/momirealms/customcrops/IAFurniture.java new file mode 100644 index 0000000..c54cb9a --- /dev/null +++ b/src/main/java/net/momirealms/customcrops/IAFurniture.java @@ -0,0 +1,30 @@ +package net.momirealms.customcrops; + +import dev.lone.itemsadder.api.CustomFurniture; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.entity.ArmorStand; +import org.bukkit.entity.Entity; + +public class IAFurniture { + + static FileConfiguration config = CustomCrops.instance.getConfig(); + + public static void placeFurniture(String name, Location location){ + CustomFurniture.spawn(name,location.getWorld().getBlockAt(location)); + } + + public static boolean getFromLocation(Location location, World world){ + for(Entity entity : world.getNearbyEntities(location,0,0,0)){ + if(entity instanceof ArmorStand){ + if(CustomFurniture.byAlreadySpawned((ArmorStand) entity) != null){ + if(CustomFurniture.byAlreadySpawned((ArmorStand) entity).getNamespacedID().equalsIgnoreCase(config.getString("config.sprinkler-1")) || CustomFurniture.byAlreadySpawned((ArmorStand) entity).getNamespacedID().equalsIgnoreCase(config.getString("config.sprinkler-2"))){ + return true; + } + } + } + } + return false; + } +} diff --git a/src/main/java/net/momirealms/customcrops/MessageManager.java b/src/main/java/net/momirealms/customcrops/MessageManager.java new file mode 100644 index 0000000..8a4eda6 --- /dev/null +++ b/src/main/java/net/momirealms/customcrops/MessageManager.java @@ -0,0 +1,26 @@ +package net.momirealms.customcrops; + +import net.kyori.adventure.audience.Audience; +import net.kyori.adventure.platform.bukkit.BukkitAudiences; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.minimessage.MiniMessage; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; + +public class MessageManager{ + + public static void consoleMessage(String s, CommandSender sender) { + BukkitAudiences bukkitAudiences = BukkitAudiences.create(CustomCrops.instance); + Audience player = bukkitAudiences.sender(sender); + MiniMessage mm = MiniMessage.miniMessage(); + Component parsed = mm.deserialize(s); + player.sendMessage(parsed); + } + public static void playerMessage(String s, Player player){ + BukkitAudiences bukkitAudiences = BukkitAudiences.create(CustomCrops.instance); + Audience p = bukkitAudiences.player(player); + MiniMessage mm = MiniMessage.miniMessage(); + Component parsed = mm.deserialize(s); + p.sendMessage(parsed); + } +} diff --git a/src/main/java/net/momirealms/customcrops/Placeholders.java b/src/main/java/net/momirealms/customcrops/Placeholders.java new file mode 100644 index 0000000..1ff0ec8 --- /dev/null +++ b/src/main/java/net/momirealms/customcrops/Placeholders.java @@ -0,0 +1,47 @@ +package net.momirealms.customcrops; + +import me.clip.placeholderapi.expansion.PlaceholderExpansion; +import org.bukkit.OfflinePlayer; +import org.bukkit.configuration.file.FileConfiguration; +import org.jetbrains.annotations.NotNull; + +public class Placeholders extends PlaceholderExpansion{ + + + private CustomCrops plugin; + + public Placeholders(CustomCrops customCrops) { + this.plugin = plugin; + } + + + @Override + public @NotNull String getIdentifier() { + return "customcrops"; + } + + @Override + public @NotNull String getAuthor() { + return "XiaoMoMi"; + } + + @Override + public @NotNull String getVersion() { + return "1.0"; + } + + @Override + public String onRequest(OfflinePlayer player, String params) { + + FileConfiguration config = CustomCrops.instance.getConfig(); + + if(params.equalsIgnoreCase("season")){ + return config.getString("current-season") + .replace("spring", config.getString("messages.spring")) + .replace("summer", config.getString("messages.summer")) + .replace("autumn", config.getString("messages.autumn")) + .replace("winter", config.getString("messages.winter")); + } + return null; + } +} diff --git a/src/main/java/net/momirealms/customcrops/listener/BreakCustomBlock.java b/src/main/java/net/momirealms/customcrops/listener/BreakCustomBlock.java new file mode 100644 index 0000000..434fb63 --- /dev/null +++ b/src/main/java/net/momirealms/customcrops/listener/BreakCustomBlock.java @@ -0,0 +1,42 @@ +package net.momirealms.customcrops.listener; + +import dev.lone.itemsadder.api.CustomBlock; +import dev.lone.itemsadder.api.Events.CustomBlockBreakEvent; +import net.momirealms.customcrops.CustomCrops; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; + +public class BreakCustomBlock implements Listener { + + FileConfiguration config = CustomCrops.instance.getConfig(); + + @EventHandler + public void breakCustomBlock(CustomBlockBreakEvent event){ + Player player =event.getPlayer(); + Location location = event.getBlock().getLocation(); + if(event.getNamespacedID().contains("stage")){ + if(player.getInventory().getItemInMainHand().containsEnchantment(Enchantment.SILK_TOUCH) || player.getInventory().getItemInMainHand().getType() == Material.SHEARS){ + event.setCancelled(true); + CustomBlock.place(event.getNamespacedID(), location); + CustomBlock.byAlreadyPlaced(location.getWorld().getBlockAt(location)).getLoot().forEach(itemStack -> { + location.getWorld().dropItem(location.clone().add(0.5,0.2,0.5), itemStack); + CustomBlock.remove(location); + }); + } + }else if(event.getNamespacedID().equalsIgnoreCase(config.getString("config.watered-pot")) || event.getNamespacedID().equalsIgnoreCase(config.getString("config.pot"))){ + if(CustomBlock.byAlreadyPlaced(location.getWorld().getBlockAt(location.clone().add(0,1,0))) != null){ + if(CustomBlock.byAlreadyPlaced(location.getWorld().getBlockAt(location.clone().add(0,1,0))).getNamespacedID().contains("stage")){ + CustomBlock.byAlreadyPlaced(location.getWorld().getBlockAt(location.clone().add(0,1,0))).getLoot().forEach(itemStack -> { + location.getWorld().dropItem(location.clone().add(0.5,1.2,0.5), itemStack); + }); + CustomBlock.remove(location.clone().add(0,1,0)); + } + } + } + } +} diff --git a/src/main/java/net/momirealms/customcrops/listener/BreakFurniture.java b/src/main/java/net/momirealms/customcrops/listener/BreakFurniture.java new file mode 100644 index 0000000..7f792f1 --- /dev/null +++ b/src/main/java/net/momirealms/customcrops/listener/BreakFurniture.java @@ -0,0 +1,32 @@ +package net.momirealms.customcrops.listener; + +import dev.lone.itemsadder.api.CustomStack; +import net.momirealms.customcrops.CustomCrops; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Item; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.entity.EntitySpawnEvent; + +public class BreakFurniture implements Listener { + + FileConfiguration config = CustomCrops.instance.getConfig(); + + @EventHandler + public void breakFurniture(EntitySpawnEvent event){ + Entity entity = event.getEntity(); + if(!(entity instanceof Item)){ + return; + } + if(CustomStack.byItemStack(((Item) entity).getItemStack()) != null){ + if(CustomStack.byItemStack(((Item) entity).getItemStack()).getNamespacedID().equalsIgnoreCase(config.getString("config.sprinkler-1"))){ + entity.remove(); + entity.getWorld().dropItem(entity.getLocation() ,CustomStack.getInstance(config.getString("config.sprinkler-1-item")).getItemStack()); + }else if(CustomStack.byItemStack(((Item) entity).getItemStack()).getNamespacedID().equalsIgnoreCase(config.getString("config.sprinkler-2"))){ + entity.remove(); + entity.getWorld().dropItem(entity.getLocation() ,CustomStack.getInstance(config.getString("config.sprinkler-2-item")).getItemStack()); + } + } + } +} diff --git a/src/main/java/net/momirealms/customcrops/listener/RightClickBlock.java b/src/main/java/net/momirealms/customcrops/listener/RightClickBlock.java new file mode 100644 index 0000000..b9052b4 --- /dev/null +++ b/src/main/java/net/momirealms/customcrops/listener/RightClickBlock.java @@ -0,0 +1,101 @@ +package net.momirealms.customcrops.listener; + +import dev.lone.itemsadder.api.CustomStack; +import net.momirealms.customcrops.CustomCrops; +import net.momirealms.customcrops.DataManager.MaxSprinklersPerChunk; +import net.momirealms.customcrops.DataManager.SprinklerManager; +import net.momirealms.customcrops.IAFurniture; +import net.momirealms.customcrops.MessageManager; +import org.bukkit.GameMode; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.Sound; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.block.Action; +import org.bukkit.event.player.PlayerInteractEvent; +import org.bukkit.inventory.ItemStack; + +import java.util.List; + +public class RightClickBlock implements Listener { + + FileConfiguration config = CustomCrops.instance.getConfig(); + + @EventHandler + public void rightClickBlock(PlayerInteractEvent event){ + if(event.getAction() != Action.RIGHT_CLICK_BLOCK || !event.hasItem()){ + return; + } + Player player = event.getPlayer(); + ItemStack itemStack = event.getItem(); + + if(itemStack.getType() == Material.WOODEN_SWORD){ + List lineOfSight = player.getLineOfSight(null, 3); + boolean hasWater = false; + for (final Block block : lineOfSight) { + if (block.getType() == Material.WATER) { + hasWater = true; + } + } + if(hasWater){ + addWater(itemStack,player); + return; + } + } + if(event.getBlockFace() != BlockFace.UP) return; + + if(CustomStack.byItemStack(event.getItem()) == null){ + return; + } + + if(event.getClickedBlock().getY() > config.getInt("config.height.max") || event.getClickedBlock().getY() < config.getInt("config.height.min")){ + MessageManager.playerMessage(config.getString("messages.prefix") + config.getString("messages.not-a-good-place"),player); + return; + } + + Location location = event.getClickedBlock().getLocation(); + + if(CustomStack.byItemStack(event.getItem()).getNamespacedID().equalsIgnoreCase(config.getString("config.sprinkler-1-item"))){ + if(MaxSprinklersPerChunk.maxSprinklersPerChunk(location)){ + MessageManager.playerMessage(config.getString("messages.prefix")+config.getString("messages.reach-limit-sprinkler").replace("{Max}", config.getString("config.max-sprinklers")),player); + return; + } + if(player.getGameMode() != GameMode.CREATIVE){ + itemStack.setAmount(itemStack.getAmount() -1); + } + IAFurniture.placeFurniture(config.getString("config.sprinkler-1"),location.clone().add(0,1,0)); + SprinklerManager.putInstance(location.clone().add(0,1,0),"s1"); + }else if(CustomStack.byItemStack(event.getItem()).getNamespacedID().equalsIgnoreCase(config.getString("config.sprinkler-2-item"))){ + if(MaxSprinklersPerChunk.maxSprinklersPerChunk(location)){ + MessageManager.playerMessage(config.getString("messages.prefix")+config.getString("messages.reach-limit-sprinkler").replace("{Max}", config.getString("config.max-sprinklers")),player); + return; + } + if(player.getGameMode() != GameMode.CREATIVE){ + itemStack.setAmount(itemStack.getAmount() -1); + } + IAFurniture.placeFurniture(config.getString("config.sprinkler-2"),location.clone().add(0,1,0)); + SprinklerManager.putInstance(location.clone().add(0,1,0),"s2"); + } + } + private void addWater(ItemStack itemStack, Player player){ + if(CustomStack.byItemStack(itemStack)!= null){ + CustomStack customStack = CustomStack.byItemStack(itemStack); + if(customStack.getNamespacedID().equalsIgnoreCase(config.getString("config.watering-can-1")) || + customStack.getNamespacedID().equalsIgnoreCase(config.getString("config.watering-can-2")) || + customStack.getNamespacedID().equalsIgnoreCase(config.getString("config.watering-can-3"))) + { + if(customStack.getMaxDurability() == customStack.getDurability()){ + MessageManager.playerMessage(config.getString("messages.prefix") + config.getString("messages.can-full"),player); + }else { + customStack.setDurability(customStack.getDurability() + 1); + player.playSound(player, Sound.ITEM_BUCKET_FILL,1,1); + } + } + } + } +} diff --git a/src/main/java/net/momirealms/customcrops/listener/RightClickCustomBlock.java b/src/main/java/net/momirealms/customcrops/listener/RightClickCustomBlock.java new file mode 100644 index 0000000..f9f4200 --- /dev/null +++ b/src/main/java/net/momirealms/customcrops/listener/RightClickCustomBlock.java @@ -0,0 +1,232 @@ +package net.momirealms.customcrops.listener; + +import dev.lone.itemsadder.api.CustomBlock; +import dev.lone.itemsadder.api.CustomStack; +import dev.lone.itemsadder.api.Events.CustomBlockInteractEvent; +import net.momirealms.customcrops.CustomCrops; +import net.momirealms.customcrops.DataManager.CropManager; +import net.momirealms.customcrops.DataManager.MaxCropsPerChunk; +import net.momirealms.customcrops.MessageManager; +import org.bukkit.*; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.configuration.file.FileConfiguration; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.block.Action; +import org.bukkit.inventory.ItemStack; + +import java.util.Objects; + +public class RightClickCustomBlock implements Listener { + + FileConfiguration config = CustomCrops.instance.getConfig(); + + @EventHandler + public void rightClickCustomCrop(CustomBlockInteractEvent event){ + if (event.getAction() != Action.RIGHT_CLICK_BLOCK) { + return; + } + Block clickedBlock = event.getBlockClicked(); + Location clickedBlockLocation = clickedBlock.getLocation(); + CustomBlock clickedCustomBlock = CustomBlock.byAlreadyPlaced(clickedBlock); + if (event.getItem() == null) { + if (clickedCustomBlock.getNamespacedID().contains("stage")){ + if(clickedCustomBlock.getNamespacedID().equalsIgnoreCase(config.getString("config.dead-crop"))) return; + String namespace = clickedCustomBlock.getNamespacedID().split(":")[0]; + String[] cropNameList = clickedCustomBlock.getNamespacedID().split(":")[1].split("_"); + int nextStage = Integer.parseInt(cropNameList[2]) + 1; + if (CustomBlock.getInstance(namespace + ":" + cropNameList[0] + "_" +cropNameList[1] +"_" + nextStage) == null) { + clickedCustomBlock.getLoot().forEach(itemStack -> { + clickedBlockLocation.getWorld().dropItem(clickedBlockLocation.clone().add(0.5,0.2,0.5),itemStack); + }); + CustomBlock.remove(clickedBlockLocation); + if(config.getConfigurationSection("crops." + cropNameList[0]).getKeys(false).contains("return")){ + CustomBlock.place(config.getString("crops." + cropNameList[0] + ".return"), clickedBlockLocation); + } + } + } + return; + } + Player player = event.getPlayer(); + World world = player.getWorld(); + ItemStack mainHandItem = player.getInventory().getItemInMainHand(); + + + if (CustomBlock.byAlreadyPlaced(world.getBlockAt(clickedBlockLocation.clone().subtract(0,1,0))) != null && clickedBlock.getType() == Material.TRIPWIRE) { + if(CustomBlock.byAlreadyPlaced(world.getBlockAt(clickedBlockLocation.clone().subtract(0,1,0))).getNamespacedID().equalsIgnoreCase(config.getString("config.pot"))) { + if (mainHandItem.getType() == Material.WATER_BUCKET) { + if(player.getGameMode() != GameMode.CREATIVE){ + mainHandItem.setAmount(mainHandItem.getAmount() - 1); + player.getInventory().addItem(new ItemStack(Material.BUCKET)); + } + CustomBlock.remove(clickedBlockLocation.clone().subtract(0, 1, 0)); + CustomBlock.place(config.getString("config.watered-pot"), clickedBlockLocation.clone().subtract(0, 1, 0)); + }else if(mainHandItem.getType() == Material.WOODEN_SWORD){ + waterPot(mainHandItem,player,clickedBlockLocation.clone().subtract(0,1,0)); + } + } + else if(CustomBlock.byAlreadyPlaced(world.getBlockAt(clickedBlockLocation.clone().subtract(0,1,0))).getNamespacedID().equalsIgnoreCase(config.getString("config.watered-pot"))){ + if (mainHandItem.getType() == Material.BONE_MEAL){ + if (clickedCustomBlock.getNamespacedID().contains("stage")){ + String[] cropNameList = clickedCustomBlock.getNamespacedID().split("_"); + int nextStage = Integer.parseInt(cropNameList[2]) + 1; + if (CustomBlock.getInstance(cropNameList[0] + "_" +cropNameList[1] +"_" + nextStage) != null){ + if(player.getGameMode() != GameMode.CREATIVE){ + mainHandItem.setAmount(mainHandItem.getAmount() - 1); + } + if (Math.random() < config.getDouble("config.bone-meal-chance")){ + CustomBlock.remove(clickedBlockLocation); + CustomBlock.place(cropNameList[0] + "_" +cropNameList[1] +"_" + nextStage,clickedBlockLocation); + Particle particleSuccess = Particle.valueOf(config.getString("config.particle.success")); + world.spawnParticle(particleSuccess, clickedBlockLocation.clone().add(0.5, 0.1,0.5), 1 ,0,0,0,0); + if(config.getBoolean("config.bone-meal-consume-water")){ + CustomBlock.remove(clickedBlockLocation.clone().subtract(0,1,0)); + CustomBlock.place(config.getString("config.pot"), clickedBlockLocation.clone().subtract(0,1,0)); + } + }else { + Particle particleFailure = Particle.valueOf(config.getString("config.particle.failure")); + world.spawnParticle(particleFailure, clickedBlockLocation.clone().add(0.5, 0.1,0.5), 1 ,0,0,0,0); + } + } + } + } + } + } else if (CustomBlock.byAlreadyPlaced(world.getBlockAt(clickedBlockLocation)) != null && event.getBlockFace() == BlockFace.UP){ + if (CustomBlock.byAlreadyPlaced(world.getBlockAt(clickedBlockLocation)).getNamespacedID().equalsIgnoreCase(config.getString("config.pot"))){ + if (mainHandItem.getType() == Material.WATER_BUCKET){ + if(player.getGameMode() != GameMode.CREATIVE){ + mainHandItem.setAmount(mainHandItem.getAmount() - 1); + player.getInventory().addItem(new ItemStack(Material.BUCKET)); + } + CustomBlock.remove(clickedBlockLocation); + CustomBlock.place(config.getString("config.watered-pot"),clickedBlockLocation); + } else if (mainHandItem.getType() == Material.WOODEN_SWORD){ + waterPot(mainHandItem, player,clickedBlockLocation); + } else { + tryPlantSeed(clickedBlockLocation, mainHandItem, player); + } + }else if(CustomBlock.byAlreadyPlaced(world.getBlockAt(clickedBlockLocation)).getNamespacedID().equalsIgnoreCase(config.getString("config.watered-pot"))){ + + tryPlantSeed(clickedBlockLocation, mainHandItem, player); + } + } + } + private void tryPlantSeed(Location clickedBlockLocation, ItemStack mainHandItem, Player player) { + if(CustomStack.byItemStack(mainHandItem) == null) return; + if (CustomStack.byItemStack(mainHandItem).getNamespacedID().toLowerCase().endsWith("_seeds")){ + String namespaced_id = CustomStack.byItemStack(mainHandItem).getNamespacedID().toLowerCase(); + String[] crop = CustomStack.byItemStack(mainHandItem).getNamespacedID().toLowerCase().replace("_seeds","").split(":"); + if (clickedBlockLocation.getY() < config.getInt("config.height.min") || clickedBlockLocation.getY() > config.getInt("config.height.max")){ + MessageManager.playerMessage(config.getString("messages.prefix") + config.getString("messages.not-a-good-place"),player); + return; + } + Label_out: + if(config.getBoolean("enable-season")){ + if(config.getBoolean("config.enable-greenhouse")){ + for(int i = 1; i <= config.getInt("config.greenhouse-range"); i++){ + Location tempLocation = clickedBlockLocation.clone().add(0,i+1,0); + if (CustomBlock.byAlreadyPlaced(clickedBlockLocation.getWorld().getBlockAt(tempLocation)) != null){ + if(CustomBlock.byAlreadyPlaced(clickedBlockLocation.getWorld().getBlockAt(tempLocation)).getNamespacedID().equalsIgnoreCase(config.getString("config.greenhouse-glass"))){ + break Label_out; + } + } + } + } + String[] seasons = config.getString("crops."+crop[1]+".season").split(","); + boolean wrongSeason = true; + for(String season : seasons){ + if(Objects.equals(season, config.getString("current-season"))){ + wrongSeason = false; + } + } + if(wrongSeason){ + MessageManager.playerMessage(config.getString("messages.prefix")+config.getString("messages.wrong-season"),player); + return; + } + } + if(!config.contains("crops."+crop[1])){ + MessageManager.playerMessage(config.getString("messages.prefix")+config.getString("messages.no-such-seed"),player); + return; + } + if(MaxCropsPerChunk.maxCropsPerChunk(clickedBlockLocation)){ + MessageManager.playerMessage(config.getString("messages.prefix")+config.getString("messages.reach-limit-crop").replace("{Max}", config.getString("config.max-crops")),player); + return; + } + if(config.getBoolean("enable-season")){ + CropManager.putInstance(clickedBlockLocation.clone().add(0,1,0), config.getString("crops."+crop[1]+".season")); + }else{ + CropManager.putInstance(clickedBlockLocation.clone().add(0,1,0), "all"); + } + if(player.getGameMode() != GameMode.CREATIVE){ + mainHandItem.setAmount(mainHandItem.getAmount() -1); + } + CustomBlock.place(namespaced_id.replace("_seeds","_stage_1"),clickedBlockLocation.clone().add(0,1,0)); + } + } + private void waterPot(ItemStack itemStack, Player player, Location location){ + + if(CustomStack.byItemStack(itemStack) == null) return; + + CustomStack customStack = CustomStack.byItemStack(itemStack); + + if(customStack.getDurability() > 0){ + CustomStack.byItemStack(itemStack).setDurability(CustomStack.byItemStack(itemStack).getDurability() - 1); + }else return; + Bukkit.getScheduler().runTaskAsynchronously(CustomCrops.instance,()-> { + + int x; + int z; + + if (customStack.getNamespacedID().equalsIgnoreCase(config.getString("config.watering-can-1"))) { + x = 0; + z = 0; + } else if (customStack.getNamespacedID().equalsIgnoreCase(config.getString("config.watering-can-2"))) { + x = 2; + z = 2; + } else if (customStack.getNamespacedID().equalsIgnoreCase(config.getString("config.watering-can-3"))) { + x = 4; + z = 4; + } else return; + player.playSound(player,Sound.BLOCK_WATER_AMBIENT,1,1); + float yaw = player.getLocation().getYaw(); + if (yaw <= 45 && yaw >= -135) { + if (yaw > -45) { + x = 0; + } else { + z = 0; + } + for (int i = 0; i <= x; i++) { + for (int j = 0; j <= z; j++) { + Location tempLoc = location.clone().add(i, 0, j); + canWaterPot(tempLoc); + } + } + } else { + if (yaw < 135 && yaw > 45) { + z= 0; + } else { + x= 0; + } + for (int i = 0; i <= x; i++) { + for (int j = 0; j <= z; j++) { + Location tempLoc = location.clone().subtract(i, 0, j); + canWaterPot(tempLoc); + } + } + } + }); + } + private void canWaterPot(Location location){ + if(CustomBlock.byAlreadyPlaced(location.getWorld().getBlockAt(location)) != null){ + if(CustomBlock.byAlreadyPlaced(location.getWorld().getBlockAt(location)).getNamespacedID().equalsIgnoreCase(config.getString("config.pot"))){ + Bukkit.getScheduler().callSyncMethod(CustomCrops.instance,()->{ + CustomBlock.remove(location); + CustomBlock.place(config.getString("config.watered-pot"),location); + return null; + }); + } + } + } +} diff --git a/src/main/resources/config-english.yml b/src/main/resources/config-english.yml new file mode 100644 index 0000000..6eb1773 --- /dev/null +++ b/src/main/resources/config-english.yml @@ -0,0 +1,95 @@ +enable-season: true +# Current season +# Use command "/customcrops setseason" to change season +# You can experience season feature by using "CMI schedule" or other Season plugin +current-season: spring + +config: + # The pot that crops can be planted in + # format: namespace:item_id + pot: customcrops:pot + # Crops can only grow on watered pot + watered-pot: customcrops:watered_pot + + # Watering Can + # 1x1 range + watering-can-1: customcrops:watering_can_1 + # 1x3 range + watering-can-2: customcrops:watering_can_2 + # 1x5 range + watering-can-3: customcrops:watering_can_3 + + # enable greenhouse feature + # crops can be planted under greenhouse glass + enable-greenhouse: true + # effective range + greenhouse-range: 7 + greenhouse-glass: customcrops:greenhouse_glass + + # sprinkler furniture in ItemsAdder + # 3x3 range + sprinkler-1: customcrops:sprinkler_1 + # sprinkler item in ItemsAdder + sprinkler-1-item: customcrops:sprinkler_1_item + + # 5x5 range + sprinkler-2: customcrops:sprinkler_2 + sprinkler-2-item: customcrops:sprinkler_2_item + + # dead crops + dead-crop: customcrops:crop_stage_death + # The chance that crops grow every morning + grow-success-chance: 0.8 + + # The chance that using bone meal to skip one stage + bone-meal-chance: 0.5 + # Will using bone meal consume water + bone-meal-consume-water: true + # Particle Effects + particle: + # Use bone meal + success: HEART + failure: VILLAGER_ANGRY + # The height where crops can grow + height: + min: 50 + max: 100 + # should we limit the max number of crops and spinklers in chunks? + # Maybe too many crops would cause lagggggggg + enable-limit: true + max-crops: 32 + max-sprinklers: 4 + # whitelist worlds where crops would grow + whitelist-worlds: + - world + # should we only alow crops in loaded chunks to grow? + # This is good for server performance + only-grow-in-loaded-chunks: true + +messages: + prefix: '[CustomCrops] ' + not-a-good-place: 'That place is too high/low. Just try another place!' + reload: 'Reloaded' + force-save: 'Successfully save cache data!' + no-such-seed: 'This seed is not configured in config.yml' + wrong-season: 'You can not plant this crop in this season' + season-set: 'Successfully change season to {Season}' + season-disabled: 'Season is not enabled in this server' + clean-cache: 'Successfully clean removed crops and sprinkler' + reach-limit-crop: 'Too many crops. The limit is {Max} per chunk!' + reach-limit-sprinkler: 'Too many sprinklers. The limit is {Max} per chunk!' + backup: 'Back up Successfully' + spring: 'spring' + summer: 'summer' + autumn: 'autumn' + winter: 'winter' + can-full: 'The watering can is already full!' + +# Crops +crops: + tomato: + # Please name the crops with "_seeds" and "_stage_X" + # Otherwise they would not grow + # You can refer to the example configuration :) + season: spring,autumn + #return: customcrops:tomato_stage_3 \ No newline at end of file diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml new file mode 100644 index 0000000..689f635 --- /dev/null +++ b/src/main/resources/config.yml @@ -0,0 +1,95 @@ +# 是否使用季节 +# 非植物季节则无法耕种,过了季节则枯死 +enable-season: true +# 当前季节 +# 使用/customcrops setseason 来切换季节 +current-season: spring + +config: + # 植物的种植盆方块 + pot: customcrops:pot + # 植物的浇过水的种植盆方块 + watered-pot: customcrops:watered_pot + + # 水壶 + # 1x1 + watering-can-1: customcrops:watering_can_1 + # 1x3 + watering-can-2: customcrops:watering_can_2 + # 1x5 + watering-can-3: customcrops:watering_can_3 + + # 温室玻璃,在温室玻璃正下方的方块可以无视季节种植 + enable-greenhouse: true + # 温室玻璃有效范围 + greenhouse-range: 7 + # 温室玻璃方块 + greenhouse-glass: customcrops:greenhouse_glass + + # 洒水器的家具 + sprinkler-1: customcrops:sprinkler_1 + # 洒水器的方块的物品 + sprinkler-1-item: customcrops:sprinkler_1_item + + # 优质洒水器的家具 + sprinkler-2: customcrops:sprinkler_2 + # 优质洒水器的方块的物品 + sprinkler-2-item: customcrops:sprinkler_2_item + + # 骨粉催熟农作物进入下一阶段的概率(0-1) + bone-meal-chance: 0.5 + # 使用骨粉是否会让种植盆从湿润转为干燥 + bone-meal-consume-water: true + # 农作物枯萎后变成的方块,物品ID中请保留stage以保持其下方方块被破坏时,枯萎作物也被破坏的特性 + dead-crop: customcrops:crop_stage_death + # 农作物每天成长一个阶段的概率(0-1) + grow-success-chance: 0.8 + # 粒子效果 + particle: + # 使用骨粉成功的粒子 + success: HEART + # 使用骨粉失败的粒子 + failure: VILLAGER_ANGRY + # 最低和最高Y坐标 + height: + min: 50 + max: 100 + # 每个区块最大农作物数量和洒水器数量 + # 检测范围为上一个height之间,事件无法异步,所以 + # 若min与max相差越小,则种植农作物对性能的消耗越小 + enable-limit: true + max-crops: 32 + max-sprinklers: 4 + # 生长生效的世界 + whitelist-worlds: + - world + # 只在被加载的区块中生长和枯萎,对性能友好 + only-grow-in-loaded-chunks: true + +messages: + prefix: '[CustomCrops] ' + not-a-good-place: 这个地方太高/太低了,请换个地方试试吧! + reload: 插件已重载 + force-save: 成功强制保存缓存信息 + no-such-seed: 此种子未在配置文件中配置! + wrong-season: 这个季节不适合这种农作物生长! + season-set: 成功设置季节为{Season} + season-disabled: 季节系统未启用. + clean-cache: 无效农作物和洒水器数据清除完毕! + reach-limit-crop: 农作物已到达最大区块上限{Max}! + reach-limit-sprinkler: 洒水器已到达最大区块上限{Max}! + backup: 备份完成 + spring: 春 + summer: 夏 + autumn: 秋 + winter: 冬 + can-full: 水壶已满! + +# 启用的农作物 +crops: + tomato: + # 在IA配置文件中命名农作物时请以_seeds和_stage_X结尾,否则会报错 + # 适宜的生长季节,若未启用季节系统可以无视此项目 + season: spring,summer + # 空手收获后返回第几个生长状态 + #return: customcrops:tomato_stage_3 \ No newline at end of file diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml new file mode 100644 index 0000000..ada64f6 --- /dev/null +++ b/src/main/resources/plugin.yml @@ -0,0 +1,10 @@ +name: CustomCrops +version: '${project.version}' +main: net.momirealms.customcrops.CustomCrops +api-version: 1.18 +depend: [ ItemsAdder , ProtocolLib ] +softdepend: [ PlaceholderAPI ] +authors: [ XiaoMoMi ] +commands: + customcrops: + permission-message: No Permission \ No newline at end of file