diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..2e6e589 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/src/main/java/net/momirealms/customcrops/ConfigReader.java b/src/main/java/net/momirealms/customcrops/ConfigReader.java index c9cb293..a1105a2 100644 --- a/src/main/java/net/momirealms/customcrops/ConfigReader.java +++ b/src/main/java/net/momirealms/customcrops/ConfigReader.java @@ -13,7 +13,6 @@ import net.momirealms.customcrops.requirements.YPos; import net.momirealms.customcrops.utils.*; import org.apache.commons.lang.StringUtils; import org.bukkit.Bukkit; -import org.bukkit.Sound; import org.bukkit.World; import org.bukkit.configuration.file.FileConfiguration; import org.bukkit.configuration.file.YamlConfiguration; @@ -53,11 +52,6 @@ public class ConfigReader { public static List cropGrowTimeList; public static List integration; public static boolean asyncCheck; -// public static boolean useBoneMeal; -// public static boolean consumeWater; -// public static double boneMealChance; -// public static String success; -// public static String failure; public static boolean enableLimit; public static int cropLimit; public static int sprinklerLimit; @@ -71,6 +65,7 @@ public class ConfigReader { public static boolean onlyLoadedGrow; public static boolean quality; public static boolean canAddWater; + public static boolean allWorld; public static double quality_1; public static double quality_2; @@ -92,19 +87,10 @@ public class ConfigReader { timeToGrow = config.getInt("config.time-to-grow",60)*20; timeToWork = config.getInt("config.time-to-work",30)*20; - //异步读取时间 asyncCheck = config.getBoolean("config.async-time-check",false); logTime = config.getBoolean("config.log-time-consume",false); onlyLoadedGrow = !config.getBoolean("config.only-grow-in-loaded-chunks",true); - - //骨粉设置(已废弃) -// useBoneMeal = config.getBoolean("config.bone-meal.enable",false); -// if (useBoneMeal){ -// boneMealChance = config.getDouble("config.bone-meal.chance"); -// consumeWater = config.getBoolean("config.bone-meal.consume-water"); -// success = config.getString("config.bone-meal.particle.success"); -// failure = config.getString("config.bone-meal.particle.failure"); -// } + allWorld = config.getBoolean("config.all-world-grow",false); //数量与高度限制 enableLimit = config.getBoolean("config.limit.enable",true); @@ -121,7 +107,7 @@ public class ConfigReader { yMax = 320; } - //农作物品质处理 + //农作物品质 quality = config.getBoolean("config.quality.enable",true); if (quality){ String[] split = StringUtils.split(config.getString("config.quality.default-ratio","17/2/1"), "/"); diff --git a/src/main/java/net/momirealms/customcrops/CustomCrops.java b/src/main/java/net/momirealms/customcrops/CustomCrops.java index 69fc978..c098adc 100644 --- a/src/main/java/net/momirealms/customcrops/CustomCrops.java +++ b/src/main/java/net/momirealms/customcrops/CustomCrops.java @@ -4,6 +4,7 @@ import net.kyori.adventure.platform.bukkit.BukkitAudiences; import net.momirealms.customcrops.commands.Executor; import net.momirealms.customcrops.commands.Completer; import net.momirealms.customcrops.datamanager.*; +import net.momirealms.customcrops.helper.LibraryLoader; import net.momirealms.customcrops.listener.BreakBlock; import net.momirealms.customcrops.listener.InteractEntity; import net.momirealms.customcrops.listener.ItemSpawn; @@ -31,10 +32,25 @@ public final class CustomCrops extends JavaPlugin { private PotManager potManager; @Override - public void onEnable() { + public void onLoad(){ instance = this; - adventure = BukkitAudiences.create(this); + LibraryLoader.load("net.kyori","adventure-api","4.11.0","https://oss.sonatype.org/content/groups/public"); + LibraryLoader.load("net.kyori","adventure-platform-api","4.1.1","https://oss.sonatype.org/content/groups/public"); + LibraryLoader.load("net.kyori","adventure-platform-bukkit","4.1.1","https://oss.sonatype.org/content/groups/public"); + LibraryLoader.load("net.kyori","adventure-platform-facet","4.1.1","https://oss.sonatype.org/content/groups/public"); + LibraryLoader.load("net.kyori","adventure-text-minimessage","4.10.1","https://oss.sonatype.org/content/groups/public"); + LibraryLoader.load("net.kyori","adventure-text-serializer-gson","4.11.0","https://oss.sonatype.org/content/groups/public"); + LibraryLoader.load("net.kyori","adventure-text-serializer-plain","4.11.0","https://oss.sonatype.org/content/groups/public"); + LibraryLoader.load("net.kyori","adventure-text-serializer-gson-legacy-impl","4.11.0","https://oss.sonatype.org/content/groups/public"); + LibraryLoader.load("net.kyori","adventure-nbt","4.11.0","https://oss.sonatype.org/content/groups/public"); + LibraryLoader.load("net.kyori","adventure-key","4.11.0","https://oss.sonatype.org/content/groups/public"); + } + + @Override + public void onEnable() { + + adventure = BukkitAudiences.create(instance); AdventureManager.consoleMessage("[CustomCrops] Running on " + Bukkit.getVersion()); diff --git a/src/main/java/net/momirealms/customcrops/datamanager/CropManager.java b/src/main/java/net/momirealms/customcrops/datamanager/CropManager.java index 75d0438..3a77f81 100644 --- a/src/main/java/net/momirealms/customcrops/datamanager/CropManager.java +++ b/src/main/java/net/momirealms/customcrops/datamanager/CropManager.java @@ -84,6 +84,7 @@ public class CropManager { //农作物生长 public void cropGrow(String worldName){ + Long time1 = System.currentTimeMillis(); updateData(); Long time2 = System.currentTimeMillis(); @@ -209,6 +210,135 @@ public class CropManager { } } + //农作物生长 + public void cropGrowAll(){ + Long time1 = System.currentTimeMillis(); + updateData(); + Long time2 = System.currentTimeMillis(); + if(ConfigReader.Config.logTime){ + AdventureManager.consoleMessage("性能监测: 农作物数据更新" + (time2-time1) + "ms"); + } + Bukkit.getWorlds().forEach(world -> { + String worldName = world.getName(); + if (data.contains(worldName)){ + data.getConfigurationSection(worldName).getKeys(false).forEach(chunk ->{ + String[] split = StringUtils.split(chunk,","); + if (ConfigReader.Config.onlyLoadedGrow || world.isChunkLoaded(Integer.parseInt(split[0]), Integer.parseInt(split[1]))){ + data.getConfigurationSection(worldName + "." + chunk).getValues(false).forEach((key, value) -> { + String[] coordinate = StringUtils.split(key, ","); + Location seedLocation = new Location(world,Double.parseDouble(coordinate[0]),Double.parseDouble(coordinate[1]),Double.parseDouble(coordinate[2])); + CustomBlock seedBlock = CustomBlock.byAlreadyPlaced(seedLocation.getBlock()); + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append(worldName).append(".").append(chunk).append(".").append(key); + if(seedBlock == null) { + data.set(stringBuilder.toString(), null); + return; + } + String namespacedID = seedBlock.getNamespacedID(); + String id = seedBlock.getId(); + if(namespacedID.equals(ConfigReader.Basic.dead)) { + data.set(stringBuilder.toString(), null); + return; + } + if(!namespacedID.contains("_stage_")) { + data.set(stringBuilder.toString(), null); + return; + } + Location potLocation = seedLocation.clone().subtract(0,1,0); + CustomBlock pot = CustomBlock.byAlreadyPlaced(potLocation.getBlock()); + if (pot == null) return; + String potNamespacedID = pot.getNamespacedID(); + String[] cropNameList = StringUtils.split(id,"_"); + CropInstance cropInstance = ConfigReader.CROPS.get(cropNameList[0]); + int random = new Random().nextInt(ConfigReader.Config.timeToGrow); + if (potNamespacedID.equals(ConfigReader.Basic.watered_pot)){ + //如果启用季节限制且农作物有季节需求 + if (ConfigReader.Season.enable && cropInstance.getSeasons() != null){ + if (isWrongSeason(seedLocation, cropInstance.getSeasons(), worldName)){ + data.set(stringBuilder.toString(), null); + bukkitScheduler.runTaskLater(CustomCrops.instance, () -> { + CustomBlock.remove(seedLocation); + CustomBlock.place(ConfigReader.Basic.dead, seedLocation); + }, random); + return; + } + } + int nextStage = Integer.parseInt(cropNameList[2]) + 1; + if (CustomBlock.getInstance(StringUtils.chop(namespacedID) + nextStage) != null) { + Fertilizer fertilizer = PotManager.Cache.get(potLocation); + if (fertilizer != null){ + int times = fertilizer.getTimes(); + if (times > 0){ + fertilizer.setTimes(times - 1); + if (fertilizer instanceof SpeedGrow speedGrow){ + if (Math.random() < speedGrow.getChance() && CustomBlock.getInstance(StringUtils.chop(namespacedID) + (nextStage + 1)) != null){ + addStage(potLocation, seedLocation, namespacedID, nextStage + 1, random); + }else { + addStage(potLocation, seedLocation, namespacedID, nextStage, random); + } + }else if(fertilizer instanceof RetainingSoil retainingSoil){ + if (Math.random() < retainingSoil.getChance()){ + addStage(seedLocation, namespacedID, nextStage, random); + }else { + addStage(potLocation, seedLocation, namespacedID, nextStage, random); + } + }else if(fertilizer instanceof QualityCrop){ + addStage(potLocation, seedLocation, namespacedID, nextStage, random); + }else { + AdventureManager.consoleMessage("[CustomCrops] 发现未知类型肥料,已自动清除错误数据!"); + PotManager.Cache.remove(potLocation); + } + }else { + PotManager.Cache.remove(potLocation); + } + } + else { + addStage(potLocation, seedLocation, namespacedID, nextStage, random); + } + } + else if(cropInstance.getGiant() != null){ + bukkitScheduler.runTaskLater(CustomCrops.instance, () ->{ + CustomBlock.remove(potLocation); + CustomBlock.place(ConfigReader.Basic.pot, potLocation); + if(cropInstance.getGiantChance() > Math.random()){ + data.set(stringBuilder.toString(), null); + CustomBlock.remove(seedLocation); + CustomBlock.place(cropInstance.getGiant(), seedLocation); + } + }, random); + }else { + if (cropInstance.getReturnStage() == null && !ConfigReader.Season.enable) data.set(stringBuilder.toString(), null); + bukkitScheduler.runTaskLater(CustomCrops.instance, () -> { + CustomBlock.remove(potLocation); + CustomBlock.place(ConfigReader.Basic.pot, potLocation); + }, random); + } + }else if(potNamespacedID.equals(ConfigReader.Basic.pot)){ + if(!ConfigReader.Season.enable || cropInstance.getSeasons() == null) return; + if(isWrongSeason(seedLocation, cropInstance.getSeasons(), worldName)){ + data.set(stringBuilder.toString(), null); + bukkitScheduler.runTaskLater(CustomCrops.instance, () -> { + CustomBlock.remove(seedLocation); + CustomBlock.place(ConfigReader.Basic.dead, seedLocation); + }, random); + } + } + }); + } + }); + } + }); + Long time3 = System.currentTimeMillis(); + if(ConfigReader.Config.logTime){ + AdventureManager.consoleMessage("性能监测: 农作物生长过程" + (time3-time2) + "ms"); + } + saveData(); + Long time4 = System.currentTimeMillis(); + if(ConfigReader.Config.logTime){ + AdventureManager.consoleMessage("性能监测: 农作物数据保存" + (time4-time3) + "ms"); + } + } + private boolean isWrongSeason(Location seedLocation, List seasons, String worldName){ if(ConfigReader.Season.greenhouse){ for(int i = 1; i <= ConfigReader.Season.range; i++){ diff --git a/src/main/java/net/momirealms/customcrops/datamanager/SprinklerManager.java b/src/main/java/net/momirealms/customcrops/datamanager/SprinklerManager.java index 318209b..251051c 100644 --- a/src/main/java/net/momirealms/customcrops/datamanager/SprinklerManager.java +++ b/src/main/java/net/momirealms/customcrops/datamanager/SprinklerManager.java @@ -129,6 +129,62 @@ public class SprinklerManager { } } + public void sprinklerWorkAll(){ + Long time1 = System.currentTimeMillis(); + updateData(); + Long time2 = System.currentTimeMillis(); + if (ConfigReader.Config.logTime){ + AdventureManager.consoleMessage("性能监测: 洒水器数据更新" + (time2-time1) + "ms"); + } + Bukkit.getWorlds().forEach(world -> { + String worldName = world.getName(); + if (data.contains(worldName)){ + BukkitScheduler bukkitScheduler = Bukkit.getScheduler(); + data.getConfigurationSection(worldName).getKeys(false).forEach(chunk ->{ + String[] split = StringUtils.split(chunk,","); + if (ConfigReader.Config.onlyLoadedGrow || world.isChunkLoaded(Integer.parseInt(split[0]), Integer.parseInt(split[1]))) { + data.getConfigurationSection(worldName + "." + chunk).getValues(false).forEach((key, value) -> { + String[] coordinate = StringUtils.split(key, ","); + Location location = new Location(world,Double.parseDouble(coordinate[0])+0.5,Double.parseDouble(coordinate[1])+0.5,Double.parseDouble(coordinate[2])+0.5); + int random = new Random().nextInt(ConfigReader.Config.timeToWork); + if (value instanceof MemorySection map){ + bukkitScheduler.callSyncMethod(CustomCrops.instance, ()->{ + int water = (int) map.get("water"); + int range = (int) map.get("range"); + if(!IAFurniture.getFromLocation(location, world)){ + data.set(worldName + "." + chunk + "." + key, null); + return null; + } + if (water > 0){ + data.set(worldName + "." + chunk + "." + key + ".water", water - 1); + bukkitScheduler.runTaskLater(CustomCrops.instance, ()-> { + for(int i = -range; i <= range; i++){ + for (int j = -range; j <= range; j++){ + waterPot(location.clone().add(i,-1,j)); + } + } + }, random); + } + if (range == 0) data.set(worldName + "." + chunk + "." + key, null); + return null; + }); + } + }); + } + }); + } + }); + Long time3 = System.currentTimeMillis(); + if(ConfigReader.Config.logTime){ + AdventureManager.consoleMessage("性能监测: 洒水器工作过程" + (time3-time2) + "ms"); + } + saveData(); + Long time4 = System.currentTimeMillis(); + if(ConfigReader.Config.logTime){ + AdventureManager.consoleMessage("性能监测: 洒水器数据保存" + (time4-time3) + "ms"); + } + } + private void waterPot(Location potLoc) { CustomBlock cb = CustomBlock.byAlreadyPlaced(potLoc.getBlock()); if(cb != null){ diff --git a/src/main/java/net/momirealms/customcrops/fertilizer/QualityCrop.java b/src/main/java/net/momirealms/customcrops/fertilizer/QualityCrop.java index cf354b7..3a65ca9 100644 --- a/src/main/java/net/momirealms/customcrops/fertilizer/QualityCrop.java +++ b/src/main/java/net/momirealms/customcrops/fertilizer/QualityCrop.java @@ -5,7 +5,7 @@ public class QualityCrop implements Fertilizer{ private int[] chance; private String key; private int times; - private boolean before; + private final boolean before; private String name; public QualityCrop(String key, int times, int[] chance, boolean before) { diff --git a/src/main/java/net/momirealms/customcrops/fertilizer/SpeedGrow.java b/src/main/java/net/momirealms/customcrops/fertilizer/SpeedGrow.java index 8bcde54..885067c 100644 --- a/src/main/java/net/momirealms/customcrops/fertilizer/SpeedGrow.java +++ b/src/main/java/net/momirealms/customcrops/fertilizer/SpeedGrow.java @@ -5,7 +5,7 @@ public class SpeedGrow implements Fertilizer{ private double chance; private String key; private int times; - private boolean before; + private final boolean before; private String name; public SpeedGrow(String key, int times, double chance, boolean before){ diff --git a/src/main/java/net/momirealms/customcrops/helper/LibraryLoader.java b/src/main/java/net/momirealms/customcrops/helper/LibraryLoader.java new file mode 100644 index 0000000..a4e8956 --- /dev/null +++ b/src/main/java/net/momirealms/customcrops/helper/LibraryLoader.java @@ -0,0 +1,196 @@ +/* + * This file is part of helper, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.momirealms.customcrops.helper; + +import com.google.common.base.Supplier; +import com.google.common.base.Suppliers; + +import net.momirealms.customcrops.CustomCrops; +import org.apache.commons.lang.StringUtils; + +import java.io.File; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.file.Files; +import java.util.Objects; + +/** + * Resolves {@link MavenLibrary} annotations for a class, and loads the dependency + * into the classloader. + */ +@NonnullByDefault +public final class LibraryLoader { + + @SuppressWarnings("Guava") + private static final Supplier URL_INJECTOR = Suppliers.memoize(() -> URLClassLoaderAccess.create((URLClassLoader) CustomCrops.instance.getClass().getClassLoader())); + + /** + * Resolves all {@link MavenLibrary} annotations on the given object. + * + * @param object the object to load libraries for. + */ + public static void loadAll(Object object) { + loadAll(object.getClass()); + } + + /** + * Resolves all {@link MavenLibrary} annotations on the given class. + * + * @param clazz the class to load libraries for. + */ + public static void loadAll(Class clazz) { + MavenLibrary[] libs = clazz.getDeclaredAnnotationsByType(MavenLibrary.class); + if (libs == null) { + return; + } + + for (MavenLibrary lib : libs) { + load(lib.groupId(), lib.artifactId(), lib.version(), lib.repo().url()); + } + } + + public static void load(String groupId, String artifactId, String version, String repoUrl) { + load(new Dependency(groupId, artifactId, version, repoUrl)); + } + + public static void load(Dependency d) { + //Log.info(String.format("Loading dependency %s:%s:%s from %s", d.getGroupId(), d.getArtifactId(), d.getVersion(), d.getRepoUrl())); + String name = d.getArtifactId() + "-" + d.getVersion(); + + File saveLocation = new File(getLibFolder(d), name + ".jar"); + if (!saveLocation.exists()) { + + try { + Log.info("Dependency '" + name + "' is not already in the libraries folder. Attempting to download..."); + URL url = d.getUrl(); + + try (InputStream is = url.openStream()) { + Files.copy(is, saveLocation.toPath()); + Log.info("Dependency '" + name + "' successfully downloaded."); + } + + } catch (Exception e) { + e.printStackTrace(); + } + } + + if (!saveLocation.exists()) { + throw new RuntimeException("Unable to download dependency: " + d.toString()); + } + + try { + URL_INJECTOR.get().addURL(saveLocation.toURI().toURL()); + } catch (Exception e) { + throw new RuntimeException("Unable to load dependency: " + saveLocation.toString(), e); + } + } + + private static File getLibFolder(Dependency dependency) { + File pluginDataFolder = CustomCrops.instance.getDataFolder(); + File serverDir = pluginDataFolder.getParentFile().getParentFile(); + + File helperDir = new File(serverDir, "libraries"); + String[] split = StringUtils.split(dependency.getGroupId(), "."); + File jarDir = new File(helperDir, split[0] + File.separator + split[1] + File.separator + dependency.artifactId + File.separator + dependency.version ); + jarDir.mkdirs(); + return jarDir; + } + + @NonnullByDefault + public static final class Dependency { + private final String groupId; + private final String artifactId; + private final String version; + private final String repoUrl; + + public Dependency(String groupId, String artifactId, String version, String repoUrl) { + this.groupId = Objects.requireNonNull(groupId, "groupId"); + this.artifactId = Objects.requireNonNull(artifactId, "artifactId"); + this.version = Objects.requireNonNull(version, "version"); + this.repoUrl = Objects.requireNonNull(repoUrl, "repoUrl"); + } + + public String getGroupId() { + return this.groupId; + } + + public String getArtifactId() { + return this.artifactId; + } + + public String getVersion() { + return this.version; + } + + public String getRepoUrl() { + return this.repoUrl; + } + + public URL getUrl() throws MalformedURLException { + String repo = this.repoUrl; + if (!repo.endsWith("/")) { + repo += "/"; + } + repo += "%s/%s/%s/%s-%s.jar"; + + String url = String.format(repo, this.groupId.replace(".", "/"), this.artifactId, this.version, this.artifactId, this.version); + return new URL(url); + } + + @Override + public boolean equals(Object o) { + if (o == this) return true; + if (!(o instanceof Dependency)) return false; + final Dependency other = (Dependency) o; + return this.getGroupId().equals(other.getGroupId()) && + this.getArtifactId().equals(other.getArtifactId()) && + this.getVersion().equals(other.getVersion()) && + this.getRepoUrl().equals(other.getRepoUrl()); + } + + @Override + public int hashCode() { + final int PRIME = 59; + int result = 1; + result = result * PRIME + this.getGroupId().hashCode(); + result = result * PRIME + this.getArtifactId().hashCode(); + result = result * PRIME + this.getVersion().hashCode(); + result = result * PRIME + this.getRepoUrl().hashCode(); + return result; + } + + @Override + public String toString() { + return "LibraryLoader.Dependency(" + + "groupId=" + this.getGroupId() + ", " + + "artifactId=" + this.getArtifactId() + ", " + + "version=" + this.getVersion() + ", " + + "repoUrl=" + this.getRepoUrl() + ")"; + } + } +} diff --git a/src/main/java/net/momirealms/customcrops/helper/Log.java b/src/main/java/net/momirealms/customcrops/helper/Log.java new file mode 100644 index 0000000..20f208a --- /dev/null +++ b/src/main/java/net/momirealms/customcrops/helper/Log.java @@ -0,0 +1,64 @@ +/* + * This file is part of helper, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.momirealms.customcrops.helper; + +import net.momirealms.customcrops.CustomCrops; +import org.bukkit.Bukkit; + +import java.util.logging.Level; + +import javax.annotation.Nonnull; + +/** + * Utility for quickly accessing a logger instance without using {@link Bukkit#getLogger()} + */ +public final class Log { + + public static void info(@Nonnull String s) { + CustomCrops.instance.getLogger().info(s); + } + + public static void warn(@Nonnull String s) { + CustomCrops.instance.getLogger().warning(s); + } + + public static void severe(@Nonnull String s) { + CustomCrops.instance.getLogger().severe(s); + } + + public static void warn(@Nonnull String s, Throwable t) { + CustomCrops.instance.getLogger().log(Level.WARNING, s, t); + } + + public static void severe(@Nonnull String s, Throwable t) { + CustomCrops.instance.getLogger().log(Level.SEVERE, s, t); + } + + private Log() { + throw new UnsupportedOperationException("This class cannot be instantiated"); + } + +} diff --git a/src/main/java/net/momirealms/customcrops/helper/MavenLibraries.java b/src/main/java/net/momirealms/customcrops/helper/MavenLibraries.java new file mode 100644 index 0000000..4a692b3 --- /dev/null +++ b/src/main/java/net/momirealms/customcrops/helper/MavenLibraries.java @@ -0,0 +1,47 @@ +/* + * This file is part of helper, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.momirealms.customcrops.helper; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import javax.annotation.Nonnull; + +/** + * Annotation to indicate the required libraries for a class. + */ +@Documented +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface MavenLibraries { + + @Nonnull + MavenLibrary[] value() default {}; + +} diff --git a/src/main/java/net/momirealms/customcrops/helper/MavenLibrary.java b/src/main/java/net/momirealms/customcrops/helper/MavenLibrary.java new file mode 100644 index 0000000..6dc2037 --- /dev/null +++ b/src/main/java/net/momirealms/customcrops/helper/MavenLibrary.java @@ -0,0 +1,78 @@ +/* + * This file is part of helper, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.momirealms.customcrops.helper; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Repeatable; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import javax.annotation.Nonnull; + +/** + * Annotation to indicate a required library for a class. + */ +@Documented +@Repeatable(MavenLibraries.class) +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface MavenLibrary { + + /** + * The group id of the library + * + * @return the group id of the library + */ + @Nonnull + String groupId(); + + /** + * The artifact id of the library + * + * @return the artifact id of the library + */ + @Nonnull + String artifactId(); + + /** + * The version of the library + * + * @return the version of the library + */ + @Nonnull + String version(); + + /** + * The repo where the library can be obtained from + * + * @return the repo where the library can be obtained from + */ + @Nonnull + Repository repo() default @Repository(url = "https://repo1.maven.org/maven2"); + +} diff --git a/src/main/java/net/momirealms/customcrops/helper/NonnullByDefault.java b/src/main/java/net/momirealms/customcrops/helper/NonnullByDefault.java new file mode 100644 index 0000000..d1de9ac --- /dev/null +++ b/src/main/java/net/momirealms/customcrops/helper/NonnullByDefault.java @@ -0,0 +1,46 @@ +/* + * This file is part of helper, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.momirealms.customcrops.helper; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +import javax.annotation.Nonnull; +import javax.annotation.meta.TypeQualifierDefault; + +@Nonnull +@Documented +@TypeQualifierDefault({ + ElementType.FIELD, + ElementType.METHOD, + ElementType.PARAMETER +}) +@Retention(RetentionPolicy.RUNTIME) +public @interface NonnullByDefault { + +} diff --git a/src/main/java/net/momirealms/customcrops/helper/Repository.java b/src/main/java/net/momirealms/customcrops/helper/Repository.java new file mode 100644 index 0000000..cadf89b --- /dev/null +++ b/src/main/java/net/momirealms/customcrops/helper/Repository.java @@ -0,0 +1,52 @@ +/* + * This file is part of helper, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.momirealms.customcrops.helper; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import javax.annotation.Nonnull; + +/** + * Represents a maven repository. + */ +@Documented +@Target(ElementType.LOCAL_VARIABLE) +@Retention(RetentionPolicy.RUNTIME) +public @interface Repository { + + /** + * Gets the base url of the repository. + * + * @return the base url of the repository + */ + @Nonnull + String url(); + +} diff --git a/src/main/java/net/momirealms/customcrops/helper/URLClassLoaderAccess.java b/src/main/java/net/momirealms/customcrops/helper/URLClassLoaderAccess.java new file mode 100644 index 0000000..2adc36b --- /dev/null +++ b/src/main/java/net/momirealms/customcrops/helper/URLClassLoaderAccess.java @@ -0,0 +1,139 @@ +/* + * This file is part of helper, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.momirealms.customcrops.helper; + +import java.lang.reflect.Field; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.Collection; + +import javax.annotation.Nonnull; + +/** + * Provides access to {@link URLClassLoader}#addURL. + */ +public abstract class URLClassLoaderAccess { + + /** + * Creates a {@link URLClassLoaderAccess} for the given class loader. + * + * @param classLoader the class loader + * @return the access object + */ + static URLClassLoaderAccess create(URLClassLoader classLoader) { + if (Unsafe.isSupported()) { + return new Unsafe(classLoader); + } else { + return Noop.INSTANCE; + } + } + + private final URLClassLoader classLoader; + + protected URLClassLoaderAccess(URLClassLoader classLoader) { + this.classLoader = classLoader; + } + + + /** + * Adds the given URL to the class loader. + * + * @param url the URL to add + */ + public abstract void addURL(@Nonnull URL url); + + /** + * Accesses using sun.misc.Unsafe, supported on Java 9+. + * + * @author Vaishnav Anil (https://github.com/slimjar/slimjar) + */ + private static class Unsafe extends URLClassLoaderAccess { + private static final sun.misc.Unsafe UNSAFE; + + static { + sun.misc.Unsafe unsafe; + try { + Field unsafeField = sun.misc.Unsafe.class.getDeclaredField("theUnsafe"); + unsafeField.setAccessible(true); + unsafe = (sun.misc.Unsafe) unsafeField.get(null); + } catch (Throwable t) { + unsafe = null; + } + UNSAFE = unsafe; + } + + private static boolean isSupported() { + return UNSAFE != null; + } + + private final Collection unopenedURLs; + private final Collection pathURLs; + + @SuppressWarnings("unchecked") + Unsafe(URLClassLoader classLoader) { + super(classLoader); + + Collection unopenedURLs; + Collection pathURLs; + try { + Object ucp = fetchField(URLClassLoader.class, classLoader, "ucp"); + unopenedURLs = (Collection) fetchField(ucp.getClass(), ucp, "unopenedUrls"); + pathURLs = (Collection) fetchField(ucp.getClass(), ucp, "path"); + } catch (Throwable e) { + unopenedURLs = null; + pathURLs = null; + } + this.unopenedURLs = unopenedURLs; + this.pathURLs = pathURLs; + } + + private static Object fetchField(final Class clazz, final Object object, final String name) throws NoSuchFieldException { + Field field = clazz.getDeclaredField(name); + long offset = UNSAFE.objectFieldOffset(field); + return UNSAFE.getObject(object, offset); + } + + @Override + public void addURL(@Nonnull URL url) { + this.unopenedURLs.add(url); + this.pathURLs.add(url); + } + } + + private static class Noop extends URLClassLoaderAccess { + private static final Noop INSTANCE = new Noop(); + + private Noop() { + super(null); + } + + @Override + public void addURL(@Nonnull URL url) { + throw new UnsupportedOperationException(); + } + } + +} diff --git a/src/main/java/net/momirealms/customcrops/timer/TimeCheck.java b/src/main/java/net/momirealms/customcrops/timer/TimeCheck.java index 3d50107..2123d18 100644 --- a/src/main/java/net/momirealms/customcrops/timer/TimeCheck.java +++ b/src/main/java/net/momirealms/customcrops/timer/TimeCheck.java @@ -23,12 +23,21 @@ public class TimeCheck extends BukkitRunnable { } } if(time == cropGrowTime){ - Bukkit.getScheduler().runTaskAsynchronously(CustomCrops.instance, () -> { - plugin.getCropManager().cropGrow(world.getName()); - }); - Bukkit.getScheduler().runTaskLaterAsynchronously(CustomCrops.instance, ()->{ - plugin.getSprinklerManager().sprinklerWork(world.getName()); - }, ConfigReader.Config.timeToGrow); + if (ConfigReader.Config.allWorld){ + Bukkit.getScheduler().runTaskAsynchronously(CustomCrops.instance, () -> { + plugin.getCropManager().cropGrowAll(); + }); + Bukkit.getScheduler().runTaskLaterAsynchronously(CustomCrops.instance, ()->{ + plugin.getSprinklerManager().sprinklerWorkAll(); + }, ConfigReader.Config.timeToGrow); + }else { + Bukkit.getScheduler().runTaskAsynchronously(CustomCrops.instance, () -> { + plugin.getCropManager().cropGrow(world.getName()); + }); + Bukkit.getScheduler().runTaskLaterAsynchronously(CustomCrops.instance, ()->{ + plugin.getSprinklerManager().sprinklerWork(world.getName()); + }, ConfigReader.Config.timeToGrow); + } } }); }); diff --git a/src/main/java/net/momirealms/customcrops/utils/CropInstance.java b/src/main/java/net/momirealms/customcrops/utils/CropInstance.java index 357b630..992ef70 100644 --- a/src/main/java/net/momirealms/customcrops/utils/CropInstance.java +++ b/src/main/java/net/momirealms/customcrops/utils/CropInstance.java @@ -31,43 +31,18 @@ public class CropInstance { public double getGiantChance() { return this.giantChance; } public List getRequirements() {return requirements;} public List getSeasons() {return seasons;} + public String getQuality_1() { return quality_1; } + public String getQuality_2() { return quality_2; } + public String getQuality_3() { return quality_3; } + public int getMax() { return max; } + public int getMin() { return min; } - public String getQuality_1() { - return quality_1; - } - - public String getQuality_2() { - return quality_2; - } - - public String getQuality_3() { - return quality_3; - } - - public int getMax() { - return max; - } - public int getMin() { - return min; - } - - public void setReturnStage(String stage){ - this.returnStage = stage; - } - public void setGiant(String giant) {this.giant = giant; } + public void setReturnStage(String stage){ this.returnStage = stage; } + public void setGiant(String giant) { this.giant = giant; } public void setGiantChance(double giantChance) { this.giantChance = giantChance; } public void setRequirements(List requirements) { this.requirements = requirements; } public void setSeasons(List seasons) { this.seasons = seasons; } - - public void setQuality_1(String quality_1) { - this.quality_1 = quality_1; - } - - public void setQuality_2(String quality_2) { - this.quality_2 = quality_2; - } - - public void setQuality_3(String quality_3) { - this.quality_3 = quality_3; - } + public void setQuality_1(String quality_1) { this.quality_1 = quality_1; } + public void setQuality_2(String quality_2) { this.quality_2 = quality_2; } + public void setQuality_3(String quality_3) { this.quality_3 = quality_3; } } diff --git a/src/main/java/net/momirealms/customcrops/utils/IAFurniture.java b/src/main/java/net/momirealms/customcrops/utils/IAFurniture.java index 523023a..b5f05d9 100644 --- a/src/main/java/net/momirealms/customcrops/utils/IAFurniture.java +++ b/src/main/java/net/momirealms/customcrops/utils/IAFurniture.java @@ -9,12 +9,10 @@ import org.bukkit.entity.Entity; public class IAFurniture { - //放置IA自定义家具 public static void placeFurniture(String name, Location location){ CustomFurniture.spawn(name, location.getBlock()); } - //根据位置获取盔甲架,如果是洒水器返回true,否则返回false public static boolean getFromLocation(Location location, World world){ for(Entity entity : world.getNearbyEntities(location,0,0,0)){ if(entity instanceof ArmorStand armorStand){ diff --git a/src/main/java/net/momirealms/customcrops/utils/Sprinkler.java b/src/main/java/net/momirealms/customcrops/utils/Sprinkler.java index b4aa13e..dd069ef 100644 --- a/src/main/java/net/momirealms/customcrops/utils/Sprinkler.java +++ b/src/main/java/net/momirealms/customcrops/utils/Sprinkler.java @@ -4,9 +4,7 @@ public class Sprinkler { private int water; private int range; - //2D private String namespacedID_1; - //3D private String namespacedID_2; public Sprinkler(int range, int water){ @@ -17,15 +15,12 @@ public class Sprinkler { public int getWater() { return water; } - public String getNamespacedID_1() { return namespacedID_1; } - public String getNamespacedID_2() { return namespacedID_2; } - public int getRange() { return range; } @@ -33,15 +28,12 @@ public class Sprinkler { public void setRange(int range) { this.range = range; } - public void setNamespacedID_2(String namespacedID_2) { this.namespacedID_2 = namespacedID_2; } - public void setNamespacedID_1(String namespacedID_1) { this.namespacedID_1 = namespacedID_1; } - public void setWater(int water) { this.water = water; } diff --git a/src/main/java/net/momirealms/customcrops/utils/WateringCan.java b/src/main/java/net/momirealms/customcrops/utils/WateringCan.java index eaabab2..648f556 100644 --- a/src/main/java/net/momirealms/customcrops/utils/WateringCan.java +++ b/src/main/java/net/momirealms/customcrops/utils/WateringCan.java @@ -2,15 +2,8 @@ package net.momirealms.customcrops.utils; public record WateringCan(int max, int width, int length) { - public int getMax() { - return max; - } + public int getMax() { return max; } + public int getLength() { return length; } + public int getWidth() { return width; } - public int getLength() { - return length; - } - - public int getWidth() { - return width; - } } diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 3cdf188..a3488e5 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -66,4 +66,7 @@ config: #如果你使用上述三个特性中的任意一个,请不要设置为false #否则农作物生长到最后一阶段不会从数据中清除,过大的数据量 #可能会导致服务器严重卡顿 - only-grow-in-loaded-chunks: true \ No newline at end of file + only-grow-in-loaded-chunks: true + + #在此模式下白名单世界将作为触发所有世界农作物生长的时间标准 + all-world-grow: true \ No newline at end of file diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 5e9118e..921b3c1 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -1,7 +1,8 @@ name: CustomCrops -version: '1.5.8' +version: '${version}' main: net.momirealms.customcrops.CustomCrops api-version: 1.16 +authors: [ XiaoMoMi ] depend: - ItemsAdder - ProtocolLib @@ -14,7 +15,6 @@ softdepend: - PlotSquared - Towny - Lands -authors: [ XiaoMoMi ] commands: customcrops: usage: /customcrops \ No newline at end of file