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