diff --git a/.idea/codeStyles/Plexus_Code_Style.xml b/.idea/codeStyles/Plexus_Code_Style.xml index a7db3e4..3dc4a27 100644 --- a/.idea/codeStyles/Plexus_Code_Style.xml +++ b/.idea/codeStyles/Plexus_Code_Style.xml @@ -1,33 +1,8 @@ - - - - - - - - + + \ No newline at end of file diff --git a/server/build.gradle b/server/build.gradle index 1407cb8..986c687 100644 --- a/server/build.gradle +++ b/server/build.gradle @@ -1,28 +1,18 @@ plugins { - id "net.minecrell.plugin-yml.bukkit" version "0.5.3" + id "net.minecrell.plugin-yml.paper" version "0.6.0" } dependencies { library "org.projectlombok:lombok:1.18.28" annotationProcessor "org.projectlombok:lombok:1.18.28" - library "org.json:json:20230227" - library "commons-io:commons-io:2.12.0" - library "dev.morphia.morphia:morphia-core:2.3.4" - library "redis.clients:jedis:4.4.2" + library "org.json:json:20230618" + library "commons-io:commons-io:2.13.0" + library "dev.morphia.morphia:morphia-core:2.3.5" + library "redis.clients:jedis:5.0.0-beta2" library "org.mariadb.jdbc:mariadb-java-client:3.1.4" library "com.zaxxer:HikariCP:5.0.1" - library "org.apache.httpcomponents.client5:httpclient5:5.2.1" - library "org.apache.commons:commons-lang3:3.12.0" - library "org.apache.maven.resolver:maven-resolver-api:1.9.10" - library "org.apache.maven.resolver:maven-resolver-impl:1.9.10" - library "org.apache.maven.resolver:maven-resolver-connector-basic:1.9.10" - library "org.apache.maven.resolver:maven-resolver-transport-http:1.9.10" - library "org.apache.maven:maven-resolver-provider:3.9.2" - library "org.eclipse.jetty:jetty-server:11.0.15" - library "org.eclipse.jetty:jetty-servlet:11.0.15" - library "org.eclipse.jetty:jetty-proxy:11.0.15" - library "com.google.code.gson:gson:2.10.1" - compileOnly "io.papermc.paper:paper-api:1.20-R0.1-SNAPSHOT" + library "org.apache.maven.resolver:maven-resolver-transport-http:1.9.13" + compileOnly "io.papermc.paper:paper-api:1.20.1-R0.1-SNAPSHOT" compileOnly("com.github.MilkBowl:VaultAPI:1.7.1") { exclude group: "org.bukkit", module: "bukkit" } @@ -40,16 +30,31 @@ shadowJar { relocate "org.bstats", "dev.plex" } -bukkit { +paper { name = "Plex" version = project.version description = "Plex provides a new experience for freedom servers." main = "dev.plex.Plex" + loader = "dev.plex.PlexLibraryManager" website = "https://plex.us.org" authors = ["Telesphoreo", "taahanis", "supernt"] // Load BukkitTelnet and LibsDisguises before Plex so the modules register properly - softDepend = ["BukkitTelnet", "LibsDisguises", "Vault"] - apiVersion = "1.18" + apiVersion = "1.19" + generateLibrariesJson = true + serverDependencies { + 'BukkitTelnet' { + required = false + load = 'BEFORE' + } + 'LibsDisguises' { + required = false + load = 'BEFORE' + } + 'Vault' { + required = false + load = 'BEFORE' + } + } } String getGitHash() { diff --git a/server/src/main/java/dev/plex/PlexLibraryManager.java b/server/src/main/java/dev/plex/PlexLibraryManager.java new file mode 100644 index 0000000..eaea137 --- /dev/null +++ b/server/src/main/java/dev/plex/PlexLibraryManager.java @@ -0,0 +1,66 @@ +package dev.plex; + +import com.google.gson.Gson; +import io.papermc.paper.plugin.loader.PluginClasspathBuilder; +import io.papermc.paper.plugin.loader.PluginLoader; +import io.papermc.paper.plugin.loader.library.impl.MavenLibraryResolver; +import org.eclipse.aether.artifact.DefaultArtifact; +import org.eclipse.aether.graph.Dependency; +import org.eclipse.aether.repository.RemoteRepository; +import org.jetbrains.annotations.NotNull; + +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.Map; +import java.util.stream.Stream; + +public class PlexLibraryManager implements PluginLoader +{ + @Override + public void classloader(@NotNull PluginClasspathBuilder classpathBuilder) + { + + MavenLibraryResolver resolver = new MavenLibraryResolver(); + PluginLibraries pluginLibraries = load(); + pluginLibraries.asDependencies().forEach(resolver::addDependency); + pluginLibraries.asRepositories().forEach(resolver::addRepository); + // The plugin is null, a hacky way to check whether to load Jetty or not + if (new File("plugins/Plex/modules/Plex-HTTPD.jar").isFile()) + { + resolver.addDependency(new Dependency(new DefaultArtifact("org.eclipse.jetty:jetty-server:11.0.15"), null)); + resolver.addDependency(new Dependency(new DefaultArtifact("org.eclipse.jetty:jetty-servlet:11.0.15"), null)); + resolver.addDependency(new Dependency(new DefaultArtifact("org.eclipse.jetty:jetty-proxy:11.0.15"), null)); + } + classpathBuilder.addLibrary(resolver); + } + + public PluginLibraries load() + { + try (var in = getClass().getResourceAsStream("/paper-libraries.json")) + { + return new Gson().fromJson(new InputStreamReader(in, StandardCharsets.UTF_8), PluginLibraries.class); + } + catch (IOException e) + { + throw new RuntimeException(e); + } + } + + private record PluginLibraries(Map repositories, List dependencies) + { + public Stream asDependencies() + { + return dependencies.stream() + .map(d -> new Dependency(new DefaultArtifact(d), null)); + } + + public Stream asRepositories() + { + return repositories.entrySet().stream() + .map(e -> new RemoteRepository.Builder(e.getKey(), "default", e.getValue()).build()); + } + } +} diff --git a/server/src/main/java/dev/plex/module/ModuleManager.java b/server/src/main/java/dev/plex/module/ModuleManager.java index 7a07d62..82cebd6 100644 --- a/server/src/main/java/dev/plex/module/ModuleManager.java +++ b/server/src/main/java/dev/plex/module/ModuleManager.java @@ -3,7 +3,6 @@ package dev.plex.module; import com.google.common.collect.Lists; import dev.plex.Plex; import dev.plex.module.exception.ModuleLoadException; -import dev.plex.module.loader.LibraryLoader; import dev.plex.util.PlexLog; import lombok.Getter; import org.apache.logging.log4j.LogManager; @@ -24,11 +23,9 @@ import java.util.List; public class ModuleManager { private final List modules = Lists.newArrayList(); - private final LibraryLoader libraryLoader; public ModuleManager() { - this.libraryLoader = new LibraryLoader(Plex.get().getLogger()); } public void loadAllModules() diff --git a/server/src/main/java/dev/plex/module/exception/ModuleLoadException.java b/server/src/main/java/dev/plex/module/exception/ModuleLoadException.java index 0a46640..23b76a2 100644 --- a/server/src/main/java/dev/plex/module/exception/ModuleLoadException.java +++ b/server/src/main/java/dev/plex/module/exception/ModuleLoadException.java @@ -6,5 +6,4 @@ public class ModuleLoadException extends RuntimeException { super(s); } - } diff --git a/server/src/main/java/dev/plex/module/loader/CustomClassLoader.java b/server/src/main/java/dev/plex/module/loader/CustomClassLoader.java deleted file mode 100644 index 740e8fd..0000000 --- a/server/src/main/java/dev/plex/module/loader/CustomClassLoader.java +++ /dev/null @@ -1,70 +0,0 @@ -package dev.plex.module.loader; - -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.nio.file.Path; -import java.nio.file.StandardCopyOption; - -public class CustomClassLoader extends URLClassLoader -{ - /*public CustomClassLoader(URL[] urls, ClassLoader parent) { - super(urls, parent); - for (URL url : urls) { - super.addURL(url); - } - }*/ - - public CustomClassLoader(URL jarInJar, ClassLoader parent) - { - super(new URL[]{extractJar(jarInJar)}, parent); - addURL(jarInJar); - } - - - static URL extractJar(URL jarInJar) throws RuntimeException - { - // get the jar-in-jar resource - if (jarInJar == null) - { - throw new RuntimeException("Could not locate jar-in-jar"); - } - - // create a temporary file - // on posix systems by default this is only read/writable by the process owner - Path path; - try - { - path = Files.createTempFile("plex-jarinjar", ".jar.tmp"); - } - catch (IOException e) - { - throw new RuntimeException("Unable to create a temporary file", e); - } - - // mark that the file should be deleted on exit - path.toFile().deleteOnExit(); - - // copy the jar-in-jar to the temporary file path - try (InputStream in = jarInJar.openStream()) - { - Files.copy(in, path, StandardCopyOption.REPLACE_EXISTING); - } - catch (IOException e) - { - throw new RuntimeException("Unable to copy jar-in-jar to temporary path", e); - } - - try - { - return path.toUri().toURL(); - } - catch (MalformedURLException e) - { - throw new RuntimeException("Unable to get URL from path", e); - } - } -} diff --git a/server/src/main/java/dev/plex/module/loader/LibraryLoader.java b/server/src/main/java/dev/plex/module/loader/LibraryLoader.java deleted file mode 100644 index 97bcf74..0000000 --- a/server/src/main/java/dev/plex/module/loader/LibraryLoader.java +++ /dev/null @@ -1,266 +0,0 @@ -package dev.plex.module.loader; - -import com.google.common.collect.Lists; -import dev.plex.Plex; -import dev.plex.module.PlexModule; -import dev.plex.module.PlexModuleFile; -import org.apache.maven.repository.internal.MavenRepositorySystemUtils; -import org.eclipse.aether.DefaultRepositorySystemSession; -import org.eclipse.aether.RepositorySystem; -import org.eclipse.aether.artifact.Artifact; -import org.eclipse.aether.artifact.DefaultArtifact; -import org.eclipse.aether.collection.CollectRequest; -import org.eclipse.aether.connector.basic.BasicRepositoryConnectorFactory; -import org.eclipse.aether.graph.Dependency; -import org.eclipse.aether.impl.DefaultServiceLocator; -import org.eclipse.aether.repository.LocalRepository; -import org.eclipse.aether.repository.RemoteRepository; -import org.eclipse.aether.repository.RepositoryPolicy; -import org.eclipse.aether.resolution.ArtifactResult; -import org.eclipse.aether.resolution.DependencyRequest; -import org.eclipse.aether.resolution.DependencyResolutionException; -import org.eclipse.aether.resolution.DependencyResult; -import org.eclipse.aether.spi.connector.RepositoryConnectorFactory; -import org.eclipse.aether.spi.connector.transport.TransporterFactory; -import org.eclipse.aether.transfer.AbstractTransferListener; -import org.eclipse.aether.transfer.TransferCancelledException; -import org.eclipse.aether.transfer.TransferEvent; -import org.eclipse.aether.transport.http.HttpTransporterFactory; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.io.File; -import java.io.IOException; -import java.net.JarURLConnection; -import java.net.MalformedURLException; -import java.net.URL; -import java.net.URLClassLoader; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Enumeration; -import java.util.List; -import java.util.jar.JarEntry; -import java.util.jar.JarFile; -import java.util.logging.Level; -import java.util.logging.Logger; - -//TODO: doesn't work - -public class LibraryLoader -{ - private final Logger logger; - private final RepositorySystem repository; - private final DefaultRepositorySystemSession session; - private final List repositories; - - public LibraryLoader(@NotNull Logger logger) - { - this.logger = logger; - - DefaultServiceLocator locator = MavenRepositorySystemUtils.newServiceLocator(); - locator.addService(RepositoryConnectorFactory.class, BasicRepositoryConnectorFactory.class); - locator.addService(TransporterFactory.class, HttpTransporterFactory.class); - - this.repository = locator.getService(RepositorySystem.class); - this.session = MavenRepositorySystemUtils.newSession(); - - session.setChecksumPolicy(RepositoryPolicy.CHECKSUM_POLICY_FAIL); - session.setLocalRepositoryManager(repository.newLocalRepositoryManager(session, new LocalRepository("libraries"))); - session.setTransferListener(new AbstractTransferListener() - { - @Override - public void transferStarted(@NotNull TransferEvent event) throws TransferCancelledException - { - logger.log(Level.INFO, "Downloading {0}", event.getResource().getRepositoryUrl() + event.getResource().getResourceName()); - } - }); - session.setReadOnly(); - - this.repositories = repository.newResolutionRepositories(session, Arrays.asList(new RemoteRepository.Builder("central", "default", "https://repo.maven.apache.org/maven2").build())); - } - - @Nullable - public ClassLoader createLoader(@NotNull PlexModule module, @NotNull PlexModuleFile moduleFile) - { - if (moduleFile.getLibraries().isEmpty()) - { - return null; - } - logger.log(Level.INFO, "Loading libraries for {0}", new Object[]{moduleFile.getName()}); - logger.log(Level.INFO, "[{0}] Loading {1} libraries... please wait", new Object[] - { - moduleFile.getName(), moduleFile.getLibraries().size() - }); - - List dependencies = new ArrayList<>(); - List> classes = Lists.newArrayList(); - List files = Lists.newArrayList(); - for (String library : moduleFile.getLibraries()) - { - Artifact artifact = new DefaultArtifact(library); - Dependency dependency = new Dependency(artifact, null); - - dependencies.add(dependency); - } - - DependencyResult result; - try - { - result = repository.resolveDependencies(session, new DependencyRequest(new CollectRequest((Dependency) null, dependencies, repositories), null)); - } - catch (DependencyResolutionException ex) - { - throw new RuntimeException("Error resolving libraries", ex); - } - - List jarFiles = new ArrayList<>(); - for (ArtifactResult artifact : result.getArtifactResults()) - { - File file = artifact.getArtifact().getFile(); - files.add(file); - URL url; - try - { - url = file.toURI().toURL(); - } - catch (MalformedURLException ex) - { - throw new AssertionError(ex); - } - - jarFiles.add(url); - logger.log(Level.INFO, "[{0}] Loaded library {1}", new Object[] - { - moduleFile.getName(), file - }); - } - - /*List jarFiles = Lists.newArrayList(); - List artifacts = Lists.newArrayList(); - - - List> classes = new ArrayList<>(); - - for (String library : moduleFile.getLibraries()) { - Artifact artifact = new DefaultArtifact(library); - ArtifactRequest request = new ArtifactRequest(); - request.setArtifact(artifact); - request.addRepository(this.repositories.get(0)); - try { - ArtifactResult result = this.repository.resolveArtifact(this.session, request); - artifact = result.getArtifact(); - jarFiles.add(artifact.getFile().toURI().toURL()); - logger.log(Level.INFO, "Loaded library {0} for {1}", new Object[]{ - artifact.getFile().toURI().toURL().toString(), - moduleFile.getName() - }); - artifacts.add(artifact); - } catch (ArtifactResolutionException | MalformedURLException e) { - e.printStackTrace(); - } - - }*/ - logger.log(Level.INFO, "Loaded {0} libraries for {1}", new Object[]{jarFiles.size(), moduleFile.getName()}); - - // jarFiles.forEach(jar -> new CustomClassLoader(jar, Plex.class.getClassLoader())); - // jarFiles.forEach(jar -> new CustomClassLoader(jar, Plex.class.getClassLoader())); - - /*URLClassLoader loader = new URLClassLoader(jarFiles.toArray(URL[]::new), Plex.class.getClassLoader()); - - dependencies.forEach(artifact -> { - ArrayList classNames; - try { - classNames = getClassNamesFromJar(new JarFile(artifact.getArtifact().getFile())); - for (String className : classNames) { - Class classToLoad = Class.forName(className, true, loader); - classes.add(classToLoad); - } - } catch (Exception e) { - e.printStackTrace(); - } - }); - - classes.forEach(clazz -> logger.log(Level.INFO, "Loading class {0}", new Object[]{clazz.getName()}));*/ - jarFiles.forEach(url -> - { - JarURLConnection connection; - try - { - URL url2 = new URL("jar:" + url.toString() + "!/"); - /* - connection = (JarURLConnection) url2.openConnection(); - logger.log(Level.INFO, "Jar File: " + connection.getJarFileURL().toString());*/ - } - catch (IOException e) - { - e.printStackTrace(); - } - }); - return new URLClassLoader(files.stream().map(File::toURI).map(uri -> - { - try - { - return uri.toURL(); - } - catch (MalformedURLException e) - { - e.printStackTrace(); - return null; - } - }).toList().toArray(URL[]::new)/*jarFiles.stream().map(url -> { - try { - return new URL("jar:" + url.toString() + "!/"); - } catch (MalformedURLException e) { - e.printStackTrace(); - return null; - } - }).toList().toArray(URL[]::new)*/, Plex.class.getClassLoader())/*new CustomClassLoader(jarFiles.toArray(URL[]::new), Plex.class.getClassLoader())*/; - } - - /*public List> loadDependency(List paths) throws Exception { - - List> classes = new ArrayList<>(); - - for (Path path : paths) { - - URL url = path.toUri().toURL(); - URLClassLoader child = new URLClassLoader(new URL[]{url}, this.getClass().getClassLoader()); - - ArrayList classNames = getClassNamesFromJar(path.toString()); - - for (String className : classNames) { - Class classToLoad = Class.forName(className, true, child); - classes.add(classToLoad); - } - } - - return classes; - }*/ - - - private ArrayList getClassNamesFromJar(JarFile file) throws Exception - { - ArrayList classNames = new ArrayList<>(); - try - { - //Iterate through the contents of the jar file - Enumeration entries = file.entries(); - while (entries.hasMoreElements()) - { - JarEntry entry = entries.nextElement(); - //Pick file that has the extension of .class - if ((entry.getName().endsWith(".class"))) - { - String className = entry.getName().replaceAll("/", "\\."); - String myClass = className.substring(0, className.lastIndexOf('.')); - classNames.add(myClass); - } - } - } - catch (Exception e) - { - throw new Exception("Error while getting class names from jar", e); - } - return classNames; - } -}