diff --git a/src/main/java/net/momirealms/customcrops/ConfigReader.java b/src/main/java/net/momirealms/customcrops/ConfigReader.java index f92d29b..5c9a0fa 100644 --- a/src/main/java/net/momirealms/customcrops/ConfigReader.java +++ b/src/main/java/net/momirealms/customcrops/ConfigReader.java @@ -17,11 +17,18 @@ package net.momirealms.customcrops; +import dev.dejvokep.boostedyaml.YamlDocument; +import dev.dejvokep.boostedyaml.dvs.versioning.BasicVersioning; +import dev.dejvokep.boostedyaml.settings.dumper.DumperSettings; +import dev.dejvokep.boostedyaml.settings.general.GeneralSettings; +import dev.dejvokep.boostedyaml.settings.loader.LoaderSettings; +import dev.dejvokep.boostedyaml.settings.updater.UpdaterSettings; import net.kyori.adventure.key.Key; import net.momirealms.customcrops.fertilizer.Fertilizer; import net.momirealms.customcrops.fertilizer.QualityCrop; import net.momirealms.customcrops.fertilizer.RetainingSoil; import net.momirealms.customcrops.fertilizer.SpeedGrow; +import net.momirealms.customcrops.helper.Log; import net.momirealms.customcrops.integrations.protection.*; import net.momirealms.customcrops.integrations.skill.Aurelium; import net.momirealms.customcrops.integrations.skill.MMOCore; @@ -40,6 +47,7 @@ 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 ConfigReader { @@ -104,6 +112,12 @@ public class ConfigReader { public static void loadConfig(){ + try { + YamlDocument.create(new File(CustomCrops.instance.getDataFolder(), "config.yml"), CustomCrops.instance.getResource("config.yml"), GeneralSettings.DEFAULT, LoaderSettings.builder().setAutoUpdate(true).build(), DumperSettings.DEFAULT, UpdaterSettings.builder().setVersioning(new BasicVersioning("config-version")).build()); + }catch (IOException e){ + Log.warn(e.getMessage()); + } + //存读基本配置文件 CustomCrops.instance.saveDefaultConfig(); CustomCrops.instance.reloadConfig(); diff --git a/src/main/java/net/momirealms/customcrops/CustomCrops.java b/src/main/java/net/momirealms/customcrops/CustomCrops.java index cc48597..de1bac4 100644 --- a/src/main/java/net/momirealms/customcrops/CustomCrops.java +++ b/src/main/java/net/momirealms/customcrops/CustomCrops.java @@ -21,6 +21,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.*; import net.momirealms.customcrops.timer.CropTimer; import net.momirealms.customcrops.utils.AdventureManager; @@ -52,6 +53,12 @@ public final class CustomCrops extends JavaPlugin { public SeasonManager getSeasonManager() { return seasonManager; } public PotManager getPotManager() { return potManager; } + @Override + public void onLoad(){ + instance = this; + LibraryLoader.load("dev.dejvokep","boosted-yaml","1.3","https://repo.maven.apache.org/maven2/"); + } + @Override public void onEnable() { @@ -106,16 +113,16 @@ public final class CustomCrops extends JavaPlugin { this.cropManager.saveData(); this.cropManager = null; } - if (this.potManager != null){ - this.potManager.saveData(); - this.potManager = null; - } if (this.sprinklerManager != null){ this.sprinklerManager.cleanData(); this.sprinklerManager.updateData(); this.sprinklerManager.saveData(); this.sprinklerManager = null; } + if (this.potManager != null){ + this.potManager.saveData(); + this.potManager = null; + } if (ConfigReader.Season.enable && !ConfigReader.Season.seasonChange && this.seasonManager != null){ this.seasonManager.saveData(); this.seasonManager = null; 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..3125205 --- /dev/null +++ b/src/main/java/net/momirealms/customcrops/helper/LibraryLoader.java @@ -0,0 +1,201 @@ +/* + * 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; + if (split.length > 1){ + jarDir = new File(helperDir, split[0] + File.separator + split[1] + File.separator + dependency.artifactId + File.separator + dependency.version ); + }else { + jarDir = new File(helperDir, dependency.getGroupId() + 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/resources/config.yml b/src/main/resources/config.yml index 23e5eac..12a00e3 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -1,3 +1,5 @@ +#Don't change +config-version: 1 config: #=============================================================# #english spanish chinese diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 6aa14a2..8f4b517 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -21,4 +21,5 @@ softdepend: - MMOCore commands: customcrops: - usage: /customcrops \ No newline at end of file + usage: /customcrops + permission: customcrops.admin \ No newline at end of file diff --git a/src/main/resources/zh-cn/config.yml b/src/main/resources/zh-cn/config.yml index 1d2e12b..16761cf 100644 --- a/src/main/resources/zh-cn/config.yml +++ b/src/main/resources/zh-cn/config.yml @@ -1,3 +1,6 @@ +#Don't change +config-version: 1 + config: #插件兼容 integration: