package net.glowstone; import com.google.common.io.PatternFilenameFilter; import org.objectweb.asm.AnnotationVisitor; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.Opcodes; import java.io.File; import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.Enumeration; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; public class GlowPluginTypeDetector { public List<File> bukkitPlugins = new ArrayList<>(); public List<File> spongePlugins = new ArrayList<>(); public List<File> canaryPlugins = new ArrayList<>(); public List<File> forgefPlugins = new ArrayList<>(); public List<File> forgenPlugins = new ArrayList<>(); public List<File> unrecognizedPlugins = new ArrayList<>(); private File directory; private Logger logger; public GlowPluginTypeDetector(File directory, Logger logger) { this.directory = directory; this.logger = logger; } public void scan() { logger.info("Scanning plugins..."); File[] files = directory.listFiles(new PatternFilenameFilter(".+\\.jar")); if (files == null || files.length == 0) { return; } for (File file : files) { scanFile(file); } logger.info("PluginTypeDetector: found " + bukkitPlugins.size() + " Bukkit, " + spongePlugins.size() + " Sponge, " + (forgefPlugins.size() + forgenPlugins.size()) + " Forge, " + canaryPlugins.size() + " Canary, " + unrecognizedPlugins.size() + " unknown plugins (total " + files.length + ")"); if (unrecognizedPlugins.size() != 0) { for (File file : unrecognizedPlugins) { logger.warning("Unrecognized plugin: " + file.getPath()); } } } private void scanFile(File file) { boolean isBukkit = false; boolean isSponge = false; boolean isCanary = false; boolean isForgeF = false; boolean isForgeN = false; URL url; try { url = file.toURI().toURL(); } catch (MalformedURLException e) { logger.log(Level.WARNING, "PluginTypeDetector: Malformed URL: " + file, e); return; } try (ZipFile zip = new ZipFile(file); ) { Enumeration<? extends ZipEntry> entries = zip.entries(); while (entries.hasMoreElements()) { ZipEntry entryIn = entries.nextElement(); String name = entryIn.getName(); if (name.equals("plugin.yml")) { isBukkit = true; } if (name.equals("Canary.inf")) { isCanary = true; } if (name.endsWith(".class") && !entryIn.isDirectory()) { // Analyze class file ClassReader classReader = new ClassReader(zip.getInputStream(entryIn)); GlowVisitor visitor = new GlowVisitor(); classReader.accept(visitor, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES); if (visitor.isSponge) { isSponge = true; } if (visitor.isForgeF) { isForgeF = true; } if (visitor.isForgeN) { isForgeN = true; } } } } catch (IOException ex) { logger.log(Level.WARNING, "PluginTypeDetector: Error reading " + url, ex); } if (isBukkit) bukkitPlugins.add(file); if (isSponge) spongePlugins.add(file); if (isCanary) canaryPlugins.add(file); if (isForgeF) forgefPlugins.add(file); if (isForgeN) forgenPlugins.add(file); if (!isBukkit && !isSponge && !isCanary && !isForgeF && !isForgeN) unrecognizedPlugins.add(file); } private class GlowVisitor extends ClassVisitor { public boolean isSponge; public boolean isForgeF; public boolean isForgeN; public GlowVisitor() { super(Opcodes.ASM5); } @Override public AnnotationVisitor visitAnnotation(String name, boolean visible) { if (name.equals("Lorg/spongepowered/api/plugin/Plugin;")) { isSponge = true; } else if (name.equals("Lcpw/mods/fml/common/Mod;")) { // older versions isForgeF = true; } else if (name.equals("Lnet/minecraftforge/fml/common/Mod;")) { // newer isForgeN = true; } return null; } } }