dynamic library loading (500kb jar :o)

This commit is contained in:
Pyrbu 2023-10-16 04:31:25 +02:00
parent f519020c5b
commit 54094ce1d5
7 changed files with 416 additions and 47 deletions

@ -17,17 +17,18 @@ processResources {
dependencies { dependencies {
compileOnly "me.clip:placeholderapi:2.11.3" // Placeholder support compileOnly "me.clip:placeholderapi:2.11.3" // Placeholder support
implementation "com.google.code.gson:gson:2.10.1" // JSON parsing compileOnly "com.google.code.gson:gson:2.10.1" // JSON parsing
implementation "org.bstats:bstats-bukkit:3.0.2" // Plugin stats compileOnly "org.bstats:bstats-bukkit:3.0.2" // Plugin stats
implementation "com.github.robertlit:SpigotResourcesAPI:2.0" // Spigot API wrapper for update checker compileOnly "com.github.robertlit:SpigotResourcesAPI:2.0" // Spigot API wrapper for update checker
implementation "com.github.retrooper.packetevents:spigot:2.1.0-SNAPSHOT" // Packets compileOnly "com.github.retrooper.packetevents:spigot:2.1.0-SNAPSHOT" // Packets
implementation "space.arim.dazzleconf:dazzleconf-ext-snakeyaml:1.2.1" // Configs compileOnly "space.arim.dazzleconf:dazzleconf-ext-snakeyaml:1.2.1" // Configs
implementation "lol.pyr:director-adventure:2.1.1" // Commands compileOnly "lol.pyr:director-adventure:2.1.1" // Commands
// Fancy text library // Fancy text library
implementation "net.kyori:adventure-platform-bukkit:4.3.0" compileOnly "net.kyori:adventure-platform-bukkit:4.3.1"
implementation "net.kyori:adventure-text-minimessage:4.14.0" compileOnly "net.kyori:adventure-text-minimessage:4.14.0"
implementation "me.lucko:jar-relocator:1.7"
implementation project(":api") implementation project(":api")
} }
@ -35,6 +36,10 @@ shadowJar {
archivesBaseName = "ZNPCsPlus" archivesBaseName = "ZNPCsPlus"
archiveClassifier.set "" 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 "org.bstats", "lol.pyr.znpcsplus.lib.bstats"
relocate "me.robertlit.spigotresources", "lol.pyr.znpcsplus.lib.spigotresources" relocate "me.robertlit.spigotresources", "lol.pyr.znpcsplus.lib.spigotresources"
relocate "net.kyori", "lol.pyr.znpcsplus.lib.kyori" 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.MojangSkinCache;
import lol.pyr.znpcsplus.skin.cache.SkinCacheCleanTask; import lol.pyr.znpcsplus.skin.cache.SkinCacheCleanTask;
import lol.pyr.znpcsplus.tasks.HologramRefreshTask; import lol.pyr.znpcsplus.tasks.HologramRefreshTask;
import lol.pyr.znpcsplus.tasks.ViewableHideOnLeaveListener;
import lol.pyr.znpcsplus.tasks.NpcProcessorTask; import lol.pyr.znpcsplus.tasks.NpcProcessorTask;
import lol.pyr.znpcsplus.tasks.ViewableHideOnLeaveListener;
import lol.pyr.znpcsplus.updater.UpdateChecker; import lol.pyr.znpcsplus.updater.UpdateChecker;
import lol.pyr.znpcsplus.updater.UpdateNotificationListener; import lol.pyr.znpcsplus.updater.UpdateNotificationListener;
import lol.pyr.znpcsplus.user.UserListener; 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.Component;
import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.format.TextColor; import net.kyori.adventure.text.format.TextColor;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import org.bstats.bukkit.Metrics; import org.bstats.bukkit.Metrics;
import org.bukkit.*; import org.bukkit.*;
import org.bukkit.plugin.Plugin; import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.PluginDescriptionFile;
import org.bukkit.plugin.PluginManager; import org.bukkit.plugin.PluginManager;
import org.bukkit.plugin.java.JavaPlugin;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.io.Reader;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.StandardOpenOption; 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() private final LegacyComponentSerializer textSerializer = LegacyComponentSerializer.builder()
.character('&') .character('&')
.hexCharacter('#') .hexCharacter('#')
.hexColors().build(); .hexColors().build();
private final List<Runnable> shutdownTasks = new ArrayList<>(); private final List<Runnable> shutdownTasks = new ArrayList<>();
private PacketEventsAPI<Plugin> packetEvents; private final PacketEventsAPI<Plugin> packetEvents;
private final ZNpcsPlusBootstrap bootstrap;
@Override public ZNpcsPlus(ZNpcsPlusBootstrap bootstrap) {
public void onLoad() { this.bootstrap = bootstrap;
packetEvents = SpigotPacketEventsBuilder.build(this); packetEvents = SpigotPacketEventsBuilder.build(bootstrap);
PacketEvents.setAPI(packetEvents); PacketEvents.setAPI(packetEvents);
packetEvents.getSettings().checkForUpdates(false); packetEvents.getSettings().checkForUpdates(false);
packetEvents.load(); packetEvents.load();
@ -88,7 +90,6 @@ public class ZNpcsPlus extends JavaPlugin {
Bukkit.getConsoleSender().sendMessage(str); Bukkit.getConsoleSender().sendMessage(str);
} }
@Override
public void onEnable() { public void onEnable() {
getDataFolder().mkdirs(); getDataFolder().mkdirs();
@ -106,7 +107,7 @@ public class ZNpcsPlus extends JavaPlugin {
} catch (IOException e) { } catch (IOException e) {
log(ChatColor.RED + " * Moving legacy files to subfolder failed, plugin will shut down."); log(ChatColor.RED + " * Moving legacy files to subfolder failed, plugin will shut down.");
e.printStackTrace(); e.printStackTrace();
pluginManager.disablePlugin(this); pluginManager.disablePlugin(bootstrap);
return; return;
} }
@ -114,12 +115,12 @@ public class ZNpcsPlus extends JavaPlugin {
packetEvents.init(); packetEvents.init();
BukkitAudiences adventure = BukkitAudiences.create(this); BukkitAudiences adventure = BukkitAudiences.create(bootstrap);
shutdownTasks.add(adventure::close); shutdownTasks.add(adventure::close);
log(ChatColor.WHITE + " * Initializing components..."); 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); shutdownTasks.add(scheduler::cancelAll);
ConfigManager configManager = new ConfigManager(getDataFolder()); ConfigManager configManager = new ConfigManager(getDataFolder());
@ -137,7 +138,7 @@ public class ZNpcsPlus extends JavaPlugin {
UserManager userManager = new UserManager(); UserManager userManager = new UserManager();
shutdownTasks.add(userManager::shutdown); shutdownTasks.add(userManager::shutdown);
BungeeConnector bungeeConnector = new BungeeConnector(this); BungeeConnector bungeeConnector = new BungeeConnector(bootstrap);
DataImporterRegistry importerRegistry = new DataImporterRegistry(configManager, adventure, DataImporterRegistry importerRegistry = new DataImporterRegistry(configManager, adventure,
scheduler, packetFactory, textSerializer, typeRegistry, getDataFolder().getParentFile(), scheduler, packetFactory, textSerializer, typeRegistry, getDataFolder().getParentFile(),
propertyRegistry, skinCache, npcRegistry, bungeeConnector); propertyRegistry, skinCache, npcRegistry, bungeeConnector);
@ -150,23 +151,23 @@ public class ZNpcsPlus extends JavaPlugin {
typeRegistry.registerDefault(packetEvents, propertyRegistry); typeRegistry.registerDefault(packetEvents, propertyRegistry);
actionRegistry.registerTypes(scheduler, adventure, textSerializer, bungeeConnector); actionRegistry.registerTypes(scheduler, adventure, textSerializer, bungeeConnector);
packetEvents.getEventManager().registerListener(new InteractionPacketListener(userManager, npcRegistry, scheduler), PacketListenerPriority.MONITOR); packetEvents.getEventManager().registerListener(new InteractionPacketListener(userManager, npcRegistry, scheduler), PacketListenerPriority.MONITOR);
new Metrics(this, 18244); new Metrics(bootstrap, 18244);
pluginManager.registerEvents(new UserListener(userManager), this); pluginManager.registerEvents(new UserListener(userManager), bootstrap);
registerCommands(npcRegistry, skinCache, adventure, actionRegistry, registerCommands(npcRegistry, skinCache, adventure, actionRegistry,
typeRegistry, propertyRegistry, importerRegistry, configManager); typeRegistry, propertyRegistry, importerRegistry, configManager);
log(ChatColor.WHITE + " * Starting tasks..."); log(ChatColor.WHITE + " * Starting tasks...");
if (configManager.getConfig().checkForUpdates()) { if (configManager.getConfig().checkForUpdates()) {
UpdateChecker updateChecker = new UpdateChecker(this.getDescription()); UpdateChecker updateChecker = new UpdateChecker(getDescription());
scheduler.runDelayedTimerAsync(updateChecker, 5L, 6000L); 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 NpcProcessorTask(npcRegistry, configManager, propertyRegistry), 60L, 3L);
scheduler.runDelayedTimerAsync(new HologramRefreshTask(npcRegistry), 60L, 20L); scheduler.runDelayedTimerAsync(new HologramRefreshTask(npcRegistry), 60L, 20L);
scheduler.runDelayedTimerAsync(new SkinCacheCleanTask(skinCache), 1200, 1200); scheduler.runDelayedTimerAsync(new SkinCacheCleanTask(skinCache), 1200, 1200);
pluginManager.registerEvents(new ViewableHideOnLeaveListener(), this); pluginManager.registerEvents(new ViewableHideOnLeaveListener(), bootstrap);
log(ChatColor.WHITE + " * Loading data..."); log(ChatColor.WHITE + " * Loading data...");
npcRegistry.reload(); 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(ChatColor.WHITE + " * Loading complete! (" + (System.currentTimeMillis() - before) + "ms)");
log(""); log("");
@ -208,7 +209,6 @@ public class ZNpcsPlus extends JavaPlugin {
} }
} }
@Override
public void onDisable() { public void onDisable() {
NpcApiProvider.unregister(); NpcApiProvider.unregister();
for (Runnable runnable : shutdownTasks) runnable.run(); for (Runnable runnable : shutdownTasks) runnable.run();
@ -238,7 +238,7 @@ public class ZNpcsPlus extends JavaPlugin {
ConfigManager configManager) { ConfigManager configManager) {
Message<CommandContext> incorrectUsageMessage = context -> context.send(Component.text("Incorrect usage: /" + context.getUsage(), NamedTextColor.RED)); 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(NpcTypeImpl.class, new NpcTypeParser(incorrectUsageMessage, typeRegistry));
manager.registerParser(NpcEntryImpl.class, new NpcEntryParser(npcRegistry, incorrectUsageMessage)); manager.registerParser(NpcEntryImpl.class, new NpcEntryParser(npcRegistry, incorrectUsageMessage));
@ -280,7 +280,7 @@ public class ZNpcsPlus extends JavaPlugin {
registerEnumParser(manager, RabbitType.class, incorrectUsageMessage); registerEnumParser(manager, RabbitType.class, incorrectUsageMessage);
registerEnumParser(manager, AttachDirection.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("center", new CenterCommand(npcRegistry))
.addSubcommand("create", new CreateCommand(npcRegistry, typeRegistry)) .addSubcommand("create", new CreateCommand(npcRegistry, typeRegistry))
.addSubcommand("reloadconfig", new ReloadConfigCommand(configManager)) .addSubcommand("reloadconfig", new ReloadConfigCommand(configManager))
@ -292,14 +292,14 @@ public class ZNpcsPlus extends JavaPlugin {
.addSubcommand("list", new ListCommand(npcRegistry)) .addSubcommand("list", new ListCommand(npcRegistry))
.addSubcommand("near", new NearCommand(npcRegistry)) .addSubcommand("near", new NearCommand(npcRegistry))
.addSubcommand("type", new TypeCommand(npcRegistry, typeRegistry)) .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("set", new PropertySetCommand(npcRegistry))
.addSubcommand("remove", new PropertyRemoveCommand(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("save", new SaveAllCommand(npcRegistry))
.addSubcommand("reload", new LoadAllCommand(npcRegistry)) .addSubcommand("reload", new LoadAllCommand(npcRegistry))
.addSubcommand("import", new ImportCommand(npcRegistry, importerRegistry))) .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("add", new HoloAddCommand(npcRegistry))
.addSubcommand("additem", new HoloAddItemCommand(npcRegistry)) .addSubcommand("additem", new HoloAddItemCommand(npcRegistry))
.addSubcommand("delete", new HoloDeleteCommand(npcRegistry)) .addSubcommand("delete", new HoloDeleteCommand(npcRegistry))
@ -310,7 +310,7 @@ public class ZNpcsPlus extends JavaPlugin {
.addSubcommand("setitem", new HoloSetItemCommand(npcRegistry)) .addSubcommand("setitem", new HoloSetItemCommand(npcRegistry))
.addSubcommand("offset", new HoloOffsetCommand(npcRegistry)) .addSubcommand("offset", new HoloOffsetCommand(npcRegistry))
.addSubcommand("refreshdelay", new HoloRefreshDelayCommand(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("add", new ActionAddCommand(npcRegistry, actionRegistry))
.addSubcommand("clear", new ActionClearCommand(npcRegistry)) .addSubcommand("clear", new ActionClearCommand(npcRegistry))
.addSubcommand("delete", new ActionDeleteCommand(npcRegistry)) .addSubcommand("delete", new ActionDeleteCommand(npcRegistry))
@ -323,10 +323,11 @@ public class ZNpcsPlus extends JavaPlugin {
manager.registerParser(clazz, new EnumParser<>(clazz, message)); manager.registerParser(clazz, new EnumParser<>(clazz, message));
} }
private Message<CommandContext> loadHelpMessage(String name) { public File getDataFolder() {
Reader reader = getTextResource("help-messages/" + name + ".txt"); return bootstrap.getDataFolder();
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 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; package lol.pyr.znpcsplus.updater;
import lol.pyr.znpcsplus.ZNpcsPlus; import lol.pyr.znpcsplus.ZNpcsPlus;
import lol.pyr.znpcsplus.scheduling.TaskScheduler;
import net.kyori.adventure.platform.bukkit.BukkitAudiences; import net.kyori.adventure.platform.bukkit.BukkitAudiences;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.event.ClickEvent; import net.kyori.adventure.text.event.ClickEvent;
import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.format.NamedTextColor;
import org.bukkit.Bukkit;
import org.bukkit.event.EventHandler; import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener; import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.event.player.PlayerJoinEvent;
@ -14,18 +14,20 @@ public class UpdateNotificationListener implements Listener {
private final ZNpcsPlus plugin; private final ZNpcsPlus plugin;
private final BukkitAudiences adventure; private final BukkitAudiences adventure;
private final UpdateChecker updateChecker; 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.plugin = plugin;
this.adventure = adventure; this.adventure = adventure;
this.updateChecker = updateChecker; this.updateChecker = updateChecker;
this.scheduler = scheduler;
} }
@EventHandler @EventHandler
public void onJoin(PlayerJoinEvent event) { public void onJoin(PlayerJoinEvent event) {
if (!event.getPlayer().hasPermission("znpcsplus.updates")) return; if (!event.getPlayer().hasPermission("znpcsplus.updates")) return;
if (updateChecker.getStatus() != UpdateChecker.Status.UPDATE_NEEDED) return; if (updateChecker.getStatus() != UpdateChecker.Status.UPDATE_NEEDED) return;
Bukkit.getScheduler().runTaskLater(plugin, () -> { scheduler.runLaterAsync(() -> {
if (!event.getPlayer().isOnline()) return; if (!event.getPlayer().isOnline()) return;
adventure.player(event.getPlayer()) adventure.player(event.getPlayer())
.sendMessage(Component.text(plugin.getDescription().getName() + " v" + updateChecker.getLatestVersion() + " is available now!", NamedTextColor.GOLD).appendNewline() .sendMessage(Component.text(plugin.getDescription().getName() + " v" + updateChecker.getLatestVersion() + " is available now!", NamedTextColor.GOLD).appendNewline()

@ -3,7 +3,7 @@ authors:
- Pyr - Pyr
- D3v1s0m - D3v1s0m
main: lol.pyr.znpcsplus.ZNpcsPlus main: lol.pyr.znpcsplus.ZNpcsPlusBootstrap
load: POSTWORLD load: POSTWORLD
version: ${version} version: ${version}