diff --git a/src/main/java/net/raphimc/viaproxy/plugins/PluginManager.java b/src/main/java/net/raphimc/viaproxy/plugins/PluginManager.java index ebf59ae..dc3cabd 100644 --- a/src/main/java/net/raphimc/viaproxy/plugins/PluginManager.java +++ b/src/main/java/net/raphimc/viaproxy/plugins/PluginManager.java @@ -17,15 +17,21 @@ */ package net.raphimc.viaproxy.plugins; +import com.vdurmont.semver4j.Semver; +import net.lenni0451.classtransform.TransformerManager; +import net.lenni0451.classtransform.additionalclassprovider.GuavaClassPathProvider; +import net.lenni0451.classtransform.utils.loader.InjectionClassLoader; +import net.lenni0451.classtransform.utils.tree.IClassProvider; import net.lenni0451.lambdaevents.LambdaManager; import net.lenni0451.lambdaevents.generator.LambdaMetaFactoryGenerator; +import net.raphimc.viaproxy.ViaProxy; +import net.raphimc.viaproxy.util.URLClassProvider; import net.raphimc.viaproxy.util.logging.Logger; import org.yaml.snakeyaml.Yaml; import java.io.File; import java.io.InputStream; import java.net.URL; -import java.net.URLClassLoader; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -37,6 +43,7 @@ public class PluginManager { public static final File PLUGINS_DIR = new File("plugins"); private static final Yaml YAML = new Yaml(); + private static final IClassProvider ROOT_CLASS_PROVIDER = new GuavaClassPathProvider(); private static final List PLUGINS = new ArrayList<>(); public static List getPlugins() { @@ -64,7 +71,10 @@ public class PluginManager { } private static void loadAndScanJar(final File file) throws Throwable { - URLClassLoader loader = new URLClassLoader(new URL[]{new URL("jar:file:" + file.getAbsolutePath() + "!/")}, PluginManager.class.getClassLoader()); + URL url = file.toURI().toURL(); + URLClassProvider classProvider = new URLClassProvider(ROOT_CLASS_PROVIDER, url); + TransformerManager transformerManager = new TransformerManager(classProvider); + InjectionClassLoader loader = new InjectionClassLoader(transformerManager, PluginManager.class.getClassLoader(), url); InputStream viaproxyYml = loader.getResourceAsStream("viaproxy.yml"); if (viaproxyYml == null) throw new IllegalStateException("Plugin '" + file.getName() + "' does not have a viaproxy.yml"); Map yaml = YAML.load(viaproxyYml); @@ -72,6 +82,10 @@ public class PluginManager { if (!yaml.containsKey("author")) throw new IllegalStateException("Plugin '" + file.getName() + "' does not have a author attribute in the viaproxy.yml"); if (!yaml.containsKey("version")) throw new IllegalStateException("Plugin '" + file.getName() + "' does not have a version attribute in the viaproxy.yml"); if (!yaml.containsKey("main")) throw new IllegalStateException("Plugin '" + file.getName() + "' does not have a main attribute in the viaproxy.yml"); + Semver minVersion = new Semver(yaml.getOrDefault("minVersion", "0.0.0").toString()); + if (!ViaProxy.VERSION.equals("${version}") && minVersion.isLowerThan(new Semver(ViaProxy.VERSION))) { + throw new IllegalStateException("Plugin '" + file.getName() + "' requires a newer version of ViaProxy (v" + minVersion + ")"); + } String main = (String) yaml.get("main"); @@ -83,6 +97,7 @@ public class PluginManager { ViaProxyPlugin plugin = (ViaProxyPlugin) instance; PLUGINS.add(plugin); + plugin.registerTransformers(transformerManager, classProvider); plugin.onEnable(); Logger.LOGGER.info("Successfully loaded plugin '" + yaml.get("name") + "' by " + yaml.get("author") + " (v" + yaml.get("version") + ")"); } diff --git a/src/main/java/net/raphimc/viaproxy/plugins/ViaProxyPlugin.java b/src/main/java/net/raphimc/viaproxy/plugins/ViaProxyPlugin.java index 294bd1b..e6b8b1e 100644 --- a/src/main/java/net/raphimc/viaproxy/plugins/ViaProxyPlugin.java +++ b/src/main/java/net/raphimc/viaproxy/plugins/ViaProxyPlugin.java @@ -17,8 +17,14 @@ */ package net.raphimc.viaproxy.plugins; +import net.lenni0451.classtransform.TransformerManager; +import net.lenni0451.classtransform.utils.tree.IClassProvider; + public abstract class ViaProxyPlugin { public abstract void onEnable(); + public void registerTransformers(final TransformerManager transformerManager, final IClassProvider classProvider) { + } + } diff --git a/src/main/java/net/raphimc/viaproxy/util/URLClassProvider.java b/src/main/java/net/raphimc/viaproxy/util/URLClassProvider.java new file mode 100644 index 0000000..d7fd035 --- /dev/null +++ b/src/main/java/net/raphimc/viaproxy/util/URLClassProvider.java @@ -0,0 +1,57 @@ +/* + * This file is part of ViaProxy - https://github.com/RaphiMC/ViaProxy + * Copyright (C) 2023 RK_01/RaphiMC and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package net.raphimc.viaproxy.util; + +import net.lenni0451.classtransform.utils.ASMUtils; +import net.lenni0451.classtransform.utils.tree.IClassProvider; +import org.apache.commons.io.IOUtils; + +import java.io.InputStream; +import java.net.URL; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.function.Supplier; + +public class URLClassProvider implements IClassProvider { + + private final IClassProvider parent; + private final List urls; + + public URLClassProvider(final IClassProvider parent, final URL... urls) { + this.parent = parent; + this.urls = Arrays.asList(urls); + } + + @Override + public byte[] getClass(String name) { + for (URL url : this.urls) { + try (InputStream is = new URL("jar:" + url + "!/" + ASMUtils.slash(name) + ".class").openStream()) { + IOUtils.toByteArray(is); + } catch (Throwable ignored) { + } + } + return this.parent.getClass(name); + } + + @Override + public Map> getAllClasses() { + return this.parent.getAllClasses(); + } + +}