dynamic library loading (500kb jar :o)
This commit is contained in:
parent
f519020c5b
commit
54094ce1d5
7 changed files with 416 additions and 47 deletions
|
@ -17,17 +17,18 @@ processResources {
|
|||
|
||||
dependencies {
|
||||
compileOnly "me.clip:placeholderapi:2.11.3" // Placeholder support
|
||||
implementation "com.google.code.gson:gson:2.10.1" // JSON parsing
|
||||
implementation "org.bstats:bstats-bukkit:3.0.2" // Plugin stats
|
||||
implementation "com.github.robertlit:SpigotResourcesAPI:2.0" // Spigot API wrapper for update checker
|
||||
implementation "com.github.retrooper.packetevents:spigot:2.1.0-SNAPSHOT" // Packets
|
||||
implementation "space.arim.dazzleconf:dazzleconf-ext-snakeyaml:1.2.1" // Configs
|
||||
implementation "lol.pyr:director-adventure:2.1.1" // Commands
|
||||
compileOnly "com.google.code.gson:gson:2.10.1" // JSON parsing
|
||||
compileOnly "org.bstats:bstats-bukkit:3.0.2" // Plugin stats
|
||||
compileOnly "com.github.robertlit:SpigotResourcesAPI:2.0" // Spigot API wrapper for update checker
|
||||
compileOnly "com.github.retrooper.packetevents:spigot:2.1.0-SNAPSHOT" // Packets
|
||||
compileOnly "space.arim.dazzleconf:dazzleconf-ext-snakeyaml:1.2.1" // Configs
|
||||
compileOnly "lol.pyr:director-adventure:2.1.1" // Commands
|
||||
|
||||
// Fancy text library
|
||||
implementation "net.kyori:adventure-platform-bukkit:4.3.0"
|
||||
implementation "net.kyori:adventure-text-minimessage:4.14.0"
|
||||
compileOnly "net.kyori:adventure-platform-bukkit:4.3.1"
|
||||
compileOnly "net.kyori:adventure-text-minimessage:4.14.0"
|
||||
|
||||
implementation "me.lucko:jar-relocator:1.7"
|
||||
implementation project(":api")
|
||||
}
|
||||
|
||||
|
@ -35,6 +36,10 @@ shadowJar {
|
|||
archivesBaseName = "ZNPCsPlus"
|
||||
archiveClassifier.set ""
|
||||
|
||||
relocate "org.objectweb.asm", "lol.pyr.znpcsplus.lib.asm"
|
||||
relocate "me.lucko.jarrelocator", "lol.pyr.znpcsplus.lib.jarrelocator"
|
||||
|
||||
// When changing anything here remember to also update the bootstrap
|
||||
relocate "org.bstats", "lol.pyr.znpcsplus.lib.bstats"
|
||||
relocate "me.robertlit.spigotresources", "lol.pyr.znpcsplus.lib.spigotresources"
|
||||
relocate "net.kyori", "lol.pyr.znpcsplus.lib.kyori"
|
||||
|
|
|
@ -40,8 +40,8 @@ import lol.pyr.znpcsplus.scheduling.TaskScheduler;
|
|||
import lol.pyr.znpcsplus.skin.cache.MojangSkinCache;
|
||||
import lol.pyr.znpcsplus.skin.cache.SkinCacheCleanTask;
|
||||
import lol.pyr.znpcsplus.tasks.HologramRefreshTask;
|
||||
import lol.pyr.znpcsplus.tasks.ViewableHideOnLeaveListener;
|
||||
import lol.pyr.znpcsplus.tasks.NpcProcessorTask;
|
||||
import lol.pyr.znpcsplus.tasks.ViewableHideOnLeaveListener;
|
||||
import lol.pyr.znpcsplus.updater.UpdateChecker;
|
||||
import lol.pyr.znpcsplus.updater.UpdateNotificationListener;
|
||||
import lol.pyr.znpcsplus.user.UserListener;
|
||||
|
@ -51,34 +51,36 @@ import net.kyori.adventure.platform.bukkit.BukkitAudiences;
|
|||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import net.kyori.adventure.text.format.TextColor;
|
||||
import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
||||
import org.bstats.bukkit.Metrics;
|
||||
import org.bukkit.*;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.plugin.PluginDescriptionFile;
|
||||
import org.bukkit.plugin.PluginManager;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.Reader;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
public class ZNpcsPlus extends JavaPlugin {
|
||||
public class ZNpcsPlus {
|
||||
private final LegacyComponentSerializer textSerializer = LegacyComponentSerializer.builder()
|
||||
.character('&')
|
||||
.hexCharacter('#')
|
||||
.hexColors().build();
|
||||
|
||||
private final List<Runnable> shutdownTasks = new ArrayList<>();
|
||||
private PacketEventsAPI<Plugin> packetEvents;
|
||||
private final PacketEventsAPI<Plugin> packetEvents;
|
||||
private final ZNpcsPlusBootstrap bootstrap;
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
packetEvents = SpigotPacketEventsBuilder.build(this);
|
||||
public ZNpcsPlus(ZNpcsPlusBootstrap bootstrap) {
|
||||
this.bootstrap = bootstrap;
|
||||
packetEvents = SpigotPacketEventsBuilder.build(bootstrap);
|
||||
PacketEvents.setAPI(packetEvents);
|
||||
packetEvents.getSettings().checkForUpdates(false);
|
||||
packetEvents.load();
|
||||
|
@ -88,7 +90,6 @@ public class ZNpcsPlus extends JavaPlugin {
|
|||
Bukkit.getConsoleSender().sendMessage(str);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
getDataFolder().mkdirs();
|
||||
|
||||
|
@ -106,7 +107,7 @@ public class ZNpcsPlus extends JavaPlugin {
|
|||
} catch (IOException e) {
|
||||
log(ChatColor.RED + " * Moving legacy files to subfolder failed, plugin will shut down.");
|
||||
e.printStackTrace();
|
||||
pluginManager.disablePlugin(this);
|
||||
pluginManager.disablePlugin(bootstrap);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -114,12 +115,12 @@ public class ZNpcsPlus extends JavaPlugin {
|
|||
|
||||
packetEvents.init();
|
||||
|
||||
BukkitAudiences adventure = BukkitAudiences.create(this);
|
||||
BukkitAudiences adventure = BukkitAudiences.create(bootstrap);
|
||||
shutdownTasks.add(adventure::close);
|
||||
|
||||
log(ChatColor.WHITE + " * Initializing components...");
|
||||
|
||||
TaskScheduler scheduler = FoliaUtil.isFolia() ? new FoliaScheduler(this) : new SpigotScheduler(this);
|
||||
TaskScheduler scheduler = FoliaUtil.isFolia() ? new FoliaScheduler(bootstrap) : new SpigotScheduler(bootstrap);
|
||||
shutdownTasks.add(scheduler::cancelAll);
|
||||
|
||||
ConfigManager configManager = new ConfigManager(getDataFolder());
|
||||
|
@ -136,8 +137,8 @@ public class ZNpcsPlus extends JavaPlugin {
|
|||
|
||||
UserManager userManager = new UserManager();
|
||||
shutdownTasks.add(userManager::shutdown);
|
||||
|
||||
BungeeConnector bungeeConnector = new BungeeConnector(this);
|
||||
|
||||
BungeeConnector bungeeConnector = new BungeeConnector(bootstrap);
|
||||
DataImporterRegistry importerRegistry = new DataImporterRegistry(configManager, adventure,
|
||||
scheduler, packetFactory, textSerializer, typeRegistry, getDataFolder().getParentFile(),
|
||||
propertyRegistry, skinCache, npcRegistry, bungeeConnector);
|
||||
|
@ -150,23 +151,23 @@ public class ZNpcsPlus extends JavaPlugin {
|
|||
typeRegistry.registerDefault(packetEvents, propertyRegistry);
|
||||
actionRegistry.registerTypes(scheduler, adventure, textSerializer, bungeeConnector);
|
||||
packetEvents.getEventManager().registerListener(new InteractionPacketListener(userManager, npcRegistry, scheduler), PacketListenerPriority.MONITOR);
|
||||
new Metrics(this, 18244);
|
||||
pluginManager.registerEvents(new UserListener(userManager), this);
|
||||
new Metrics(bootstrap, 18244);
|
||||
pluginManager.registerEvents(new UserListener(userManager), bootstrap);
|
||||
|
||||
registerCommands(npcRegistry, skinCache, adventure, actionRegistry,
|
||||
typeRegistry, propertyRegistry, importerRegistry, configManager);
|
||||
|
||||
log(ChatColor.WHITE + " * Starting tasks...");
|
||||
if (configManager.getConfig().checkForUpdates()) {
|
||||
UpdateChecker updateChecker = new UpdateChecker(this.getDescription());
|
||||
UpdateChecker updateChecker = new UpdateChecker(getDescription());
|
||||
scheduler.runDelayedTimerAsync(updateChecker, 5L, 6000L);
|
||||
pluginManager.registerEvents(new UpdateNotificationListener(this, adventure, updateChecker), this);
|
||||
pluginManager.registerEvents(new UpdateNotificationListener(this, adventure, updateChecker, scheduler), bootstrap);
|
||||
}
|
||||
|
||||
scheduler.runDelayedTimerAsync(new NpcProcessorTask(npcRegistry, configManager, propertyRegistry), 60L, 3L);
|
||||
scheduler.runDelayedTimerAsync(new HologramRefreshTask(npcRegistry), 60L, 20L);
|
||||
scheduler.runDelayedTimerAsync(new SkinCacheCleanTask(skinCache), 1200, 1200);
|
||||
pluginManager.registerEvents(new ViewableHideOnLeaveListener(), this);
|
||||
pluginManager.registerEvents(new ViewableHideOnLeaveListener(), bootstrap);
|
||||
|
||||
log(ChatColor.WHITE + " * Loading data...");
|
||||
npcRegistry.reload();
|
||||
|
@ -188,7 +189,7 @@ public class ZNpcsPlus extends JavaPlugin {
|
|||
}
|
||||
}
|
||||
|
||||
NpcApiProvider.register(this, new ZNpcsPlusApi(npcRegistry, typeRegistry, propertyRegistry, skinCache));
|
||||
NpcApiProvider.register(bootstrap, new ZNpcsPlusApi(npcRegistry, typeRegistry, propertyRegistry, skinCache));
|
||||
log(ChatColor.WHITE + " * Loading complete! (" + (System.currentTimeMillis() - before) + "ms)");
|
||||
log("");
|
||||
|
||||
|
@ -208,7 +209,6 @@ public class ZNpcsPlus extends JavaPlugin {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
NpcApiProvider.unregister();
|
||||
for (Runnable runnable : shutdownTasks) runnable.run();
|
||||
|
@ -238,7 +238,7 @@ public class ZNpcsPlus extends JavaPlugin {
|
|||
ConfigManager configManager) {
|
||||
|
||||
Message<CommandContext> incorrectUsageMessage = context -> context.send(Component.text("Incorrect usage: /" + context.getUsage(), NamedTextColor.RED));
|
||||
CommandManager manager = new CommandManager(this, adventure, incorrectUsageMessage);
|
||||
CommandManager manager = new CommandManager(bootstrap, adventure, incorrectUsageMessage);
|
||||
|
||||
manager.registerParser(NpcTypeImpl.class, new NpcTypeParser(incorrectUsageMessage, typeRegistry));
|
||||
manager.registerParser(NpcEntryImpl.class, new NpcEntryParser(npcRegistry, incorrectUsageMessage));
|
||||
|
@ -280,7 +280,7 @@ public class ZNpcsPlus extends JavaPlugin {
|
|||
registerEnumParser(manager, RabbitType.class, incorrectUsageMessage);
|
||||
registerEnumParser(manager, AttachDirection.class, incorrectUsageMessage);
|
||||
|
||||
manager.registerCommand("npc", new MultiCommand(loadHelpMessage("root"))
|
||||
manager.registerCommand("npc", new MultiCommand(bootstrap.loadHelpMessage("root"))
|
||||
.addSubcommand("center", new CenterCommand(npcRegistry))
|
||||
.addSubcommand("create", new CreateCommand(npcRegistry, typeRegistry))
|
||||
.addSubcommand("reloadconfig", new ReloadConfigCommand(configManager))
|
||||
|
@ -292,14 +292,14 @@ public class ZNpcsPlus extends JavaPlugin {
|
|||
.addSubcommand("list", new ListCommand(npcRegistry))
|
||||
.addSubcommand("near", new NearCommand(npcRegistry))
|
||||
.addSubcommand("type", new TypeCommand(npcRegistry, typeRegistry))
|
||||
.addSubcommand("property", new MultiCommand(loadHelpMessage("property"))
|
||||
.addSubcommand("property", new MultiCommand(bootstrap.loadHelpMessage("property"))
|
||||
.addSubcommand("set", new PropertySetCommand(npcRegistry))
|
||||
.addSubcommand("remove", new PropertyRemoveCommand(npcRegistry)))
|
||||
.addSubcommand("storage", new MultiCommand(loadHelpMessage("storage"))
|
||||
.addSubcommand("storage", new MultiCommand(bootstrap.loadHelpMessage("storage"))
|
||||
.addSubcommand("save", new SaveAllCommand(npcRegistry))
|
||||
.addSubcommand("reload", new LoadAllCommand(npcRegistry))
|
||||
.addSubcommand("import", new ImportCommand(npcRegistry, importerRegistry)))
|
||||
.addSubcommand("holo", new MultiCommand(loadHelpMessage("holo"))
|
||||
.addSubcommand("holo", new MultiCommand(bootstrap.loadHelpMessage("holo"))
|
||||
.addSubcommand("add", new HoloAddCommand(npcRegistry))
|
||||
.addSubcommand("additem", new HoloAddItemCommand(npcRegistry))
|
||||
.addSubcommand("delete", new HoloDeleteCommand(npcRegistry))
|
||||
|
@ -310,7 +310,7 @@ public class ZNpcsPlus extends JavaPlugin {
|
|||
.addSubcommand("setitem", new HoloSetItemCommand(npcRegistry))
|
||||
.addSubcommand("offset", new HoloOffsetCommand(npcRegistry))
|
||||
.addSubcommand("refreshdelay", new HoloRefreshDelayCommand(npcRegistry)))
|
||||
.addSubcommand("action", new MultiCommand(loadHelpMessage("action"))
|
||||
.addSubcommand("action", new MultiCommand(bootstrap.loadHelpMessage("action"))
|
||||
.addSubcommand("add", new ActionAddCommand(npcRegistry, actionRegistry))
|
||||
.addSubcommand("clear", new ActionClearCommand(npcRegistry))
|
||||
.addSubcommand("delete", new ActionDeleteCommand(npcRegistry))
|
||||
|
@ -323,10 +323,11 @@ public class ZNpcsPlus extends JavaPlugin {
|
|||
manager.registerParser(clazz, new EnumParser<>(clazz, message));
|
||||
}
|
||||
|
||||
private Message<CommandContext> loadHelpMessage(String name) {
|
||||
Reader reader = getTextResource("help-messages/" + name + ".txt");
|
||||
if (reader == null) throw new RuntimeException(name + ".txt is missing from the help-messages folder in the ZNPCsPlus jar!");
|
||||
Component component = MiniMessage.miniMessage().deserialize(FileUtil.dumpReaderAsString(reader));
|
||||
return context -> context.send(component);
|
||||
public File getDataFolder() {
|
||||
return bootstrap.getDataFolder();
|
||||
}
|
||||
|
||||
public PluginDescriptionFile getDescription() {
|
||||
return bootstrap.getDescription();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,90 @@
|
|||
package lol.pyr.znpcsplus;
|
||||
|
||||
import lol.pyr.director.adventure.command.CommandContext;
|
||||
import lol.pyr.director.common.message.Message;
|
||||
import lol.pyr.znpcsplus.libraries.LibraryLoader;
|
||||
import lol.pyr.znpcsplus.util.FileUtil;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.minimessage.MiniMessage;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.Reader;
|
||||
|
||||
public class ZNpcsPlusBootstrap extends JavaPlugin {
|
||||
private ZNpcsPlus zNpcsPlus;
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
getLogger().info("Downloading and loading libraries, this might take a while if this is the first time you're launching the plugin");
|
||||
LibraryLoader loader = new LibraryLoader(this, new File(getDataFolder(), "libraries"));
|
||||
|
||||
loader.addRelocation(decrypt("org..bstats"), "lol.pyr.znpcsplus.lib.bstats");
|
||||
loader.addRelocation(decrypt("me..robertlit..spigotresources"), "lol.pyr.znpcsplus.lib.spigotresources");
|
||||
loader.addRelocation(decrypt("net..kyori"), "lol.pyr.znpcsplus.lib.kyori");
|
||||
loader.addRelocation(decrypt("org..checkerframework"), "lol.pyr.znpcsplus.lib.checkerframework");
|
||||
loader.addRelocation(decrypt("com..google"), "lol.pyr.znpcsplus.lib.google");
|
||||
loader.addRelocation(decrypt("com..github..retrooper..packetevents"), "lol.pyr.znpcsplus.lib.packetevents.api");
|
||||
loader.addRelocation(decrypt("io..github..retrooper..packetevents"), "lol.pyr.znpcsplus.lib.packetevents.impl");
|
||||
loader.addRelocation(decrypt("org..yaml..snakeyaml"), "lol.pyr.znpcsplus.lib.snakeyaml");
|
||||
loader.addRelocation(decrypt("space..arim..dazzleconf"), "lol.pyr.znpcsplus.lib.dazzleconf");
|
||||
loader.addRelocation(decrypt("lol..pyr..director"), "lol.pyr.znpcsplus.lib.command");
|
||||
|
||||
loader.loadLibrary(decrypt("com..google..guava"), "guava", "18.0");
|
||||
loader.loadLibrary(decrypt("com..google..code..gson"), "gson", "2.10.1");
|
||||
|
||||
loader.loadLibrary(decrypt("org..bstats"), "bstats-base", "3.0.2");
|
||||
loader.loadLibrary(decrypt("org..bstats"), "bstats-bukkit", "3.0.2");
|
||||
|
||||
loader.loadLibrary("com.github.robertlit", "SpigotResourcesAPI", "2.0", "https://jitpack.io");
|
||||
|
||||
loader.loadSnapshotLibrary(decrypt("com..github..retrooper..packetevents"), "api", "2.1.0-SNAPSHOT", "2.1.0-20231011.171910-4", "https://repo.codemc.io/repository/maven-snapshots/");
|
||||
loader.loadSnapshotLibrary(decrypt("com..github..retrooper..packetevents"), "spigot", "2.1.0-SNAPSHOT", "2.1.0-20231011.171910-4", "https://repo.codemc.io/repository/maven-snapshots/");
|
||||
|
||||
loader.loadLibrary(decrypt("space..arim..dazzleconf"), "dazzleconf-core", "1.2.1");
|
||||
loader.loadLibrary(decrypt("space..arim..dazzleconf"), "dazzleconf-ext-snakeyaml", "1.2.1");
|
||||
loader.loadLibrary("org.yaml", "snakeyaml", "1.33");
|
||||
|
||||
loader.loadLibrary("lol.pyr", "director-adventure", "2.1.1", "https://repo.pyr.lol/releases");
|
||||
|
||||
loader.loadLibrary(decrypt("net..kyori"), "adventure-api", "4.14.0");
|
||||
loader.loadLibrary(decrypt("net..kyori"), "adventure-key", "4.14.0");
|
||||
loader.loadLibrary(decrypt("net..kyori"), "adventure-nbt", "4.14.0");
|
||||
loader.loadLibrary(decrypt("net..kyori"), "adventure-platform-facet", "4.3.1");
|
||||
loader.loadLibrary(decrypt("net..kyori"), "adventure-platform-api", "4.3.1");
|
||||
loader.loadLibrary(decrypt("net..kyori"), "adventure-platform-bukkit", "4.3.1");
|
||||
loader.loadLibrary(decrypt("net..kyori"), "adventure-text-minimessage", "4.14.0");
|
||||
loader.loadLibrary(decrypt("net..kyori"), "adventure-text-serializer-bungeecord", "4.3.1");
|
||||
loader.loadLibrary(decrypt("net..kyori"), "adventure-text-serializer-gson", "4.14.0");
|
||||
loader.loadLibrary(decrypt("net..kyori"), "adventure-text-serializer-json", "4.14.0");
|
||||
loader.loadLibrary(decrypt("net..kyori"), "adventure-text-serializer-legacy", "4.14.0");
|
||||
loader.loadLibrary(decrypt("net..kyori"), "examination-api", "1.3.0");
|
||||
loader.loadLibrary(decrypt("net..kyori"), "examination-string", "1.3.0");
|
||||
loader.deleteUnloadedLibraries();
|
||||
|
||||
getLogger().info("Loaded " + loader.loadedLibraryCount() + " libraries!");
|
||||
zNpcsPlus = new ZNpcsPlus(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
if (zNpcsPlus != null) zNpcsPlus.onEnable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
if (zNpcsPlus != null) zNpcsPlus.onDisable();
|
||||
}
|
||||
|
||||
protected Message<CommandContext> loadHelpMessage(String name) {
|
||||
Reader reader = getTextResource("help-messages/" + name + ".txt");
|
||||
if (reader == null) throw new RuntimeException(name + ".txt is missing from the help-messages folder in the ZNPCsPlus jar!");
|
||||
Component component = MiniMessage.miniMessage().deserialize(FileUtil.dumpReaderAsString(reader));
|
||||
return context -> context.send(component);
|
||||
}
|
||||
|
||||
// Ugly hack because of https://github.com/johnrengelman/shadow/issues/232
|
||||
private static String decrypt(String packageName) {
|
||||
return packageName.replace("..", ".");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,119 @@
|
|||
package lol.pyr.znpcsplus.libraries;
|
||||
|
||||
import me.lucko.jarrelocator.JarRelocator;
|
||||
import me.lucko.jarrelocator.Relocation;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.nio.file.Files;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class LibraryLoader {
|
||||
private final static Logger logger = Logger.getLogger("ZNPCsPlus Library Loader");
|
||||
|
||||
private final UrlClassLoaderAccess loaderAccess;
|
||||
private final File librariesFolder;
|
||||
private final Set<File> loadedLibraries = new HashSet<>();
|
||||
private final List<Relocation> relocationRules = new ArrayList<>();
|
||||
|
||||
public LibraryLoader(Plugin plugin, File librariesFolder) {
|
||||
loaderAccess = UrlClassLoaderAccess.create((URLClassLoader) plugin.getClass().getClassLoader());
|
||||
this.librariesFolder = librariesFolder;
|
||||
if (!librariesFolder.exists()) librariesFolder.mkdirs();
|
||||
}
|
||||
|
||||
public void deleteUnloadedLibraries() {
|
||||
File[] files = librariesFolder.listFiles();
|
||||
if (files == null) return;
|
||||
for (File file : files) if (!loadedLibraries.contains(file)) file.delete();
|
||||
}
|
||||
|
||||
public void addRelocation(String pre, String post) {
|
||||
relocationRules.add(new Relocation(pre, post));
|
||||
}
|
||||
|
||||
public void loadSnapshotLibrary(String groupId, String artifactId, String version, String snapshotVersion, String repoUrl) {
|
||||
try {
|
||||
loadLibrary(groupId + ":" + artifactId + ":" + version,
|
||||
getDependencyFile(groupId, artifactId, version),
|
||||
getSnapshotDependencyUrl(groupId, artifactId, version, snapshotVersion, repoUrl));
|
||||
} catch (MalformedURLException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public int loadedLibraryCount() {
|
||||
return loadedLibraries.size();
|
||||
}
|
||||
|
||||
public void loadLibrary(String groupId, String artifactId, String version) {
|
||||
loadLibrary(groupId, artifactId, version, "https://repo1.maven.org/maven2");
|
||||
}
|
||||
|
||||
public void loadLibrary(String groupId, String artifactId, String version, String repoUrl) {
|
||||
try {
|
||||
loadLibrary(groupId + ":" + artifactId + ":" + version,
|
||||
getDependencyFile(groupId, artifactId, version),
|
||||
getDependencyUrl(groupId, artifactId, version, repoUrl));
|
||||
} catch (MalformedURLException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void loadLibrary(String name, File file, URL url) {
|
||||
if (!file.exists()) {
|
||||
try (InputStream in = url.openStream()) {
|
||||
File temp = new File(file.getParentFile(), file.getName() + ".temp");
|
||||
Files.copy(in, temp.toPath());
|
||||
new JarRelocator(temp, file, relocationRules).run();
|
||||
temp.delete();
|
||||
// logger.info("Downloaded library " + name);
|
||||
} catch (IOException e) {
|
||||
logger.severe("Failed to download library " + name);
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
loaderAccess.addURL(file.toURI().toURL());
|
||||
loadedLibraries.add(file);
|
||||
// logger.info("Loaded library " + name);
|
||||
} catch (Exception e) {
|
||||
logger.severe("Failed to load library, plugin may not work correctly (" + name + ")");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private File getDependencyFile(String groupId, String artifactId, String version) {
|
||||
return new File(librariesFolder, groupId.replace(".", "-") + "-"
|
||||
+ artifactId.replace(".", "-") + "-"
|
||||
+ version.replace(".", "-") + ".jar");
|
||||
}
|
||||
|
||||
private static URL getDependencyUrl(String groupId, String artifactId, String version, String repoUrl) throws MalformedURLException {
|
||||
String url = repoUrl.endsWith("/") ? repoUrl : repoUrl + "/";
|
||||
url += groupId.replace(".", "/") + "/";
|
||||
url += artifactId + "/";
|
||||
url += version + "/";
|
||||
url += artifactId + "-" + version + ".jar";
|
||||
return new URL(url);
|
||||
}
|
||||
|
||||
private static URL getSnapshotDependencyUrl(String groupId, String artifactId, String version, String snapshotVersion, String repoUrl) throws MalformedURLException {
|
||||
String url = repoUrl.endsWith("/") ? repoUrl : repoUrl + "/";
|
||||
url += groupId.replace(".", "/") + "/";
|
||||
url += artifactId + "/";
|
||||
url += version + "/";
|
||||
url += artifactId + "-" + snapshotVersion + ".jar";
|
||||
return new URL(url);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,152 @@
|
|||
package lol.pyr.znpcsplus.libraries;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* Provides access to {@link URLClassLoader}#addURL.
|
||||
* From https://github.com/lucko/helper/blob/master/helper/src/main/java/me/lucko/helper/maven/URLClassLoaderAccess.java
|
||||
*/
|
||||
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 (Reflection.isSupported()) {
|
||||
return new Reflection(classLoader);
|
||||
} else 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 reflection, not supported on Java 9+.
|
||||
*/
|
||||
private static class Reflection extends UrlClassLoaderAccess {
|
||||
private static final Method ADD_URL_METHOD;
|
||||
|
||||
static {
|
||||
Method addUrlMethod;
|
||||
try {
|
||||
addUrlMethod = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
|
||||
addUrlMethod.setAccessible(true);
|
||||
} catch (Exception e) {
|
||||
addUrlMethod = null;
|
||||
}
|
||||
ADD_URL_METHOD = addUrlMethod;
|
||||
}
|
||||
|
||||
private static boolean isSupported() {
|
||||
return ADD_URL_METHOD != null;
|
||||
}
|
||||
|
||||
Reflection(URLClassLoader classLoader) {
|
||||
super(classLoader);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addURL(@Nonnull URL url) {
|
||||
try {
|
||||
ADD_URL_METHOD.invoke(super.classLoader, url);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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<URL> unopenedURLs;
|
||||
private final Collection<URL> pathURLs;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Unsafe(URLClassLoader classLoader) {
|
||||
super(classLoader);
|
||||
|
||||
Collection<URL> unopenedURLs;
|
||||
Collection<URL> pathURLs;
|
||||
try {
|
||||
Object ucp = fetchField(URLClassLoader.class, classLoader, "ucp");
|
||||
unopenedURLs = (Collection<URL>) fetchField(ucp.getClass(), ucp, "unopenedUrls");
|
||||
pathURLs = (Collection<URL>) 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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,11 +1,11 @@
|
|||
package lol.pyr.znpcsplus.updater;
|
||||
|
||||
import lol.pyr.znpcsplus.ZNpcsPlus;
|
||||
import lol.pyr.znpcsplus.scheduling.TaskScheduler;
|
||||
import net.kyori.adventure.platform.bukkit.BukkitAudiences;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.event.ClickEvent;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.PlayerJoinEvent;
|
||||
|
@ -14,22 +14,24 @@ public class UpdateNotificationListener implements Listener {
|
|||
private final ZNpcsPlus plugin;
|
||||
private final BukkitAudiences adventure;
|
||||
private final UpdateChecker updateChecker;
|
||||
private final TaskScheduler scheduler;
|
||||
|
||||
public UpdateNotificationListener(ZNpcsPlus plugin, BukkitAudiences adventure, UpdateChecker updateChecker) {
|
||||
public UpdateNotificationListener(ZNpcsPlus plugin, BukkitAudiences adventure, UpdateChecker updateChecker, TaskScheduler scheduler) {
|
||||
this.plugin = plugin;
|
||||
this.adventure = adventure;
|
||||
this.updateChecker = updateChecker;
|
||||
this.scheduler = scheduler;
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onJoin(PlayerJoinEvent event) {
|
||||
if (!event.getPlayer().hasPermission("znpcsplus.updates")) return;
|
||||
if (updateChecker.getStatus() != UpdateChecker.Status.UPDATE_NEEDED) return;
|
||||
Bukkit.getScheduler().runTaskLater(plugin, () -> {
|
||||
scheduler.runLaterAsync(() -> {
|
||||
if (!event.getPlayer().isOnline()) return;
|
||||
adventure.player(event.getPlayer())
|
||||
.sendMessage(Component.text(plugin.getDescription().getName() + " v" + updateChecker.getLatestVersion() + " is available now!", NamedTextColor.GOLD).appendNewline()
|
||||
.append(Component.text("Click this message to open the Spigot page (CLICK)", NamedTextColor.YELLOW)).clickEvent(ClickEvent.openUrl(UpdateChecker.DOWNLOAD_LINK)));
|
||||
.append(Component.text("Click this message to open the Spigot page (CLICK)", NamedTextColor.YELLOW)).clickEvent(ClickEvent.openUrl(UpdateChecker.DOWNLOAD_LINK)));
|
||||
}, 100L);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ authors:
|
|||
- Pyr
|
||||
- D3v1s0m
|
||||
|
||||
main: lol.pyr.znpcsplus.ZNpcsPlus
|
||||
main: lol.pyr.znpcsplus.ZNpcsPlusBootstrap
|
||||
load: POSTWORLD
|
||||
|
||||
version: ${version}
|
||||
|
|
Loading…
Reference in a new issue