Commit 06685158 authored by Him188's avatar Him188

Merge remote-tracking branch 'origin/master'

# Conflicts:
#	mirai-console/src/main/kotlin/net/mamoe/mirai/plugin/PluginBase.kt
parents 2f7a9cf9 60455bb0
package net.mamoe.mirai.plugin package net.mamoe.mirai.plugin
import net.mamoe.mirai.utils.DefaultLogger import net.mamoe.mirai.utils.DefaultLogger
import net.mamoe.mirai.utils.io.encodeToString import java.io.BufferedReader
import java.io.File import java.io.File
import java.io.InputStream
import java.io.InputStreamReader
import java.net.JarURLConnection
import java.net.URL import java.net.URL
import java.net.URLClassLoader
import java.util.jar.JarFile import java.util.jar.JarFile
abstract class PluginBase { abstract class PluginBase constructor() {
val dataFolder by lazy {
File(PluginManager.pluginsPath + pluginDescription.name).also { it.mkdir() }
}
open fun onLoad() { open fun onLoad() {
...@@ -24,27 +25,38 @@ abstract class PluginBase { ...@@ -24,27 +25,38 @@ abstract class PluginBase {
} }
fun getPluginManager(): PluginManager {
return PluginManager
}
private lateinit var pluginDescription: PluginDescription private lateinit var pluginDescription: PluginDescription
internal fun init(pluginDescription: PluginDescription) { internal fun init(pluginDescription: PluginDescription) {
this.pluginDescription = pluginDescription this.pluginDescription = pluginDescription
this.onLoad() this.onLoad()
} }
fun getDataFolder(): File {
return File(PluginManager.pluginsPath + pluginDescription.pluginName).also {
it.mkdirs()
}
}
} }
class PluginDescription( class PluginDescription(
val name: String, val pluginName: String,
val author: String, val pluginAuthor: String,
val basePath: String, val pluginBasePath: String,
val version: String, val pluginVersion: String,
val info: String, val pluginInfo: String,
val depends: List<String>,//插件的依赖 val depends: List<String>,//插件的依赖
internal var loaded: Boolean = false, internal var loaded: Boolean = false,
internal var noCircularDepend: Boolean = true internal var noCircularDepend: Boolean = true
) { ) {
override fun toString(): String { override fun toString(): String {
return "name: $name\nauthor: $author\npath: $basePath\nver: $version\ninfo: $info\ndepends: $depends" return "name: $pluginName\nauthor: $pluginAuthor\npath: $pluginBasePath\nver: $pluginVersion\ninfo: $pluginInfo\ndepends: $depends"
} }
companion object { companion object {
...@@ -56,7 +68,7 @@ class PluginDescription( ...@@ -56,7 +68,7 @@ class PluginDescription(
var basePath = "net.mamoe.mirai.PluginMain" var basePath = "net.mamoe.mirai.PluginMain"
var info = "Unknown" var info = "Unknown"
var version = "1.0.0" var version = "1.0.0"
val depends = mutableListOf<String>() val depends = mutableListOf<String>();
content.forEach { content.forEach {
val line = it.trim() val line = it.trim()
...@@ -72,9 +84,9 @@ class PluginDescription( ...@@ -72,9 +84,9 @@ class PluginDescription(
lowercaseLine.startsWith("info") || lowercaseLine.startsWith("information") -> { lowercaseLine.startsWith("info") || lowercaseLine.startsWith("information") -> {
info = line.substringAfter(":").trim() info = line.substringAfter(":").trim()
} }
lowercaseLine.startsWith("main") || lowercaseLine.startsWith("main") || lowercaseLine.startsWith("path") || lowercaseLine.startsWith(
lowercaseLine.startsWith("path") || "basepath"
lowercaseLine.startsWith("basepath") -> { ) -> {
basePath = line.substringAfter(":").trim() basePath = line.substringAfter(":").trim()
} }
lowercaseLine.startsWith("version") || lowercaseLine.startsWith("ver") -> { lowercaseLine.startsWith("version") || lowercaseLine.startsWith("ver") -> {
...@@ -91,6 +103,13 @@ class PluginDescription( ...@@ -91,6 +103,13 @@ class PluginDescription(
} }
class PluginClassLoader(file: File, parent: ClassLoader) : URLClassLoader(arrayOf(file.toURI().toURL()), parent) {
override fun findClass(moduleName: String?, name: String?): Class<*> {
return super.findClass(name)
}
}
object PluginManager { object PluginManager {
internal val pluginsPath = System.getProperty("user.dir") + "/plugins/".replace("//", "/").also { internal val pluginsPath = System.getProperty("user.dir") + "/plugins/".replace("//", "/").also {
File(it).mkdirs() File(it).mkdirs()
...@@ -107,32 +126,47 @@ object PluginManager { ...@@ -107,32 +126,47 @@ object PluginManager {
*/ */
fun loadPlugins() { fun loadPlugins() {
val pluginsFound: MutableMap<String, PluginDescription> = mutableMapOf() val pluginsFound: MutableMap<String, PluginDescription> = mutableMapOf()
val pluginsLocation: MutableMap<String, JarFile> = mutableMapOf() val pluginsLocation: MutableMap<String, File> = mutableMapOf()
File(pluginsPath).listFiles()?.forEach { file -> File(pluginsPath).listFiles()?.forEach { file ->
if (file != null && file.extension == "jar") { if (file != null) {
if (file.extension == "jar") {
val jar = JarFile(file) val jar = JarFile(file)
val pluginYml = val pluginYml =
jar.entries().asSequence().filter { it.name.toLowerCase().contains("plugin.yml") }.firstOrNull() jar.entries().asSequence().filter { it.name.toLowerCase().contains("plugin.yml") }.firstOrNull()
if (pluginYml == null) { if (pluginYml == null) {
logger.info("plugin.yml not found in jar " + jar.name + ", it will not be considered as a Plugin") logger.info("plugin.yml not found in jar " + jar.name + ", it will not be consider as a Plugin")
} else { } else {
val description = val url = URL("jar:file:" + file.absoluteFile + "!/" + pluginYml.name)
PluginDescription.readFromContent(URL("jar:file:" + file.absoluteFile + "!/" + pluginYml.name).openConnection().inputStream.readAllBytes().encodeToString()) val jarConnection: JarURLConnection = url
.openConnection() as JarURLConnection
val inputStream: InputStream = jarConnection.getInputStream()
val br = BufferedReader(InputStreamReader(inputStream, "UTF-8"))
var con: String?
val sb = StringBuffer()
while (br.readLine().also { con = it } != null) {
sb.append(con).append("\n")
}
val description = PluginDescription.readFromContent(sb.toString())
println(description) println(description)
pluginsFound[description.name] = description pluginsFound[description.pluginName] = description
pluginsLocation[description.name] = jar pluginsLocation[description.pluginName] = file
}
} }
} }
} }
fun checkNoCircularDepends(target: PluginDescription, needDepends: List<String>, existDepends: MutableList<String>) { fun checkNoCircularDependsCheck(
target: PluginDescription,
needDepends: List<String>,
existDepends: MutableList<String>
) {
if (!target.noCircularDepend) { if (!target.noCircularDepend) {
return return
} }
existDepends.add(target.name) existDepends.add(target.pluginName)
if (needDepends.any { existDepends.contains(it) }) { if (needDepends.any { existDepends.contains(it) }) {
target.noCircularDepend = false target.noCircularDepend = false
...@@ -142,14 +176,14 @@ object PluginManager { ...@@ -142,14 +176,14 @@ object PluginManager {
needDepends.forEach { needDepends.forEach {
if (pluginsFound.containsKey(it)) { if (pluginsFound.containsKey(it)) {
checkNoCircularDepends(pluginsFound[it]!!, pluginsFound[it]!!.depends, existDepends) checkNoCircularDependsCheck(pluginsFound[it]!!, pluginsFound[it]!!.depends, existDepends)
} }
} }
} }
pluginsFound.values.forEach { pluginsFound.values.forEach {
checkNoCircularDepends(it, it.depends, mutableListOf()) checkNoCircularDependsCheck(it, it.depends, mutableListOf())
} }
//load //load
...@@ -157,50 +191,58 @@ object PluginManager { ...@@ -157,50 +191,58 @@ object PluginManager {
fun loadPlugin(description: PluginDescription): Boolean { fun loadPlugin(description: PluginDescription): Boolean {
if (!description.noCircularDepend) { if (!description.noCircularDepend) {
logger.error("Failed to load plugin " + description.name + " because it has circular dependency") return false.also {
return false logger.error("Failed to load plugin " + description.pluginName + " because it has circular dependency")
}
} }
//load depends first //load depends first
description.depends.forEach { dependentName -> description.depends.forEach {
val dependent = pluginsFound[dependentName] if (!pluginsFound.containsKey(it)) {
if (dependent == null) { return false.also { _ ->
logger.error("Failed to load plugin " + description.name + " because it need " + dependentName + " as dependency") logger.error("Failed to load plugin " + description.pluginName + " because it need " + it + " as dependency")
return false
} }
}
val depend = pluginsFound[it]!!
//还没有加载 //还没有加载
if (!dependent.loaded && !loadPlugin(dependent)) { if (!depend.loaded) {
logger.error("Failed to load plugin " + description.name + " because " + dependentName + " as dependency failed to load") if (!loadPlugin(pluginsFound[it]!!)) {
return false return false.also { _ ->
logger.error("Failed to load plugin " + description.pluginName + " because " + it + " as dependency failed to load")
}
}
} }
} }
//在这里所有的depends都已经加载了 //在这里所有的depends都已经加载了
//real load //real load
logger.info("loading plugin " + description.name) logger.info("loading plugin " + description.pluginName)
try { try {
this.javaClass.classLoader.loadClass(description.basePath) val pluginClass =
PluginClassLoader((pluginsLocation[description.pluginName]!!), this.javaClass.classLoader)
.loadClass(description.pluginBasePath)
return try { return try {
val subClass = javaClass.asSubclass(PluginBase::class.java) val subClass = pluginClass.asSubclass(PluginBase::class.java)
val plugin: PluginBase = subClass.getDeclaredConstructor().newInstance() val plugin: PluginBase = subClass.getDeclaredConstructor().newInstance()
description.loaded = true description.loaded = true
logger.info("successfully loaded plugin " + description.name) logger.info("successfully loaded plugin " + description.pluginName)
logger.info(description.info) logger.info(description.pluginInfo)
nameToPluginBaseMap[description.name] = plugin nameToPluginBaseMap[description.pluginName] = plugin
plugin.init(description) plugin.init(description)
true true
} catch (e: ClassCastException) { } catch (e: ClassCastException) {
logger.error("failed to load plugin " + description.name + " , Main class does not extends PluginBase ") false.also {
false logger.error("failed to load plugin " + description.pluginName + " , Main class does not extends PluginBase ")
}
} }
} catch (e: ClassNotFoundException) { } catch (e: ClassNotFoundException) {
e.printStackTrace() e.printStackTrace()
logger.error("failed to load plugin " + description.name + " , Main class not found under " + description.basePath) return false.also {
return false logger.error("failed to load plugin " + description.pluginName + " , Main class not found under " + description.pluginBasePath)
}
} }
} }
......
...@@ -3,6 +3,10 @@ package net.mamoe.mirai.imageplugin; ...@@ -3,6 +3,10 @@ package net.mamoe.mirai.imageplugin;
import net.mamoe.mirai.plugin.PluginBase; import net.mamoe.mirai.plugin.PluginBase;
public class ImagePluginMain extends PluginBase { public class ImagePluginMain extends PluginBase {
static {
System.out.println("Static Loaded");
}
@Override @Override
public void onLoad() { public void onLoad() {
System.out.println("Loaded"); System.out.println("Loaded");
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment