mirror of
https://github.com/mamoe/mirai.git
synced 2025-01-08 17:20:11 +08:00
plugin supporting
This commit is contained in:
parent
3a2f0f3267
commit
60455bb0ac
@ -1,16 +1,17 @@
|
|||||||
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 constructor() {
|
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 constructor() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
@ -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(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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 {
|
|||||||
*/
|
*/
|
||||||
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) {
|
||||||
val jar = JarFile(file)
|
if (file.extension == "jar") {
|
||||||
val pluginYml =
|
val jar = JarFile(file)
|
||||||
jar.entries().asSequence().filter { it.name.toLowerCase().contains("plugin.yml") }.firstOrNull()
|
val pluginYml =
|
||||||
if (pluginYml == null) {
|
jar.entries().asSequence().filter { it.name.toLowerCase().contains("plugin.yml") }.firstOrNull()
|
||||||
logger.info("plugin.yml not found in jar " + jar.name + ", it will not be considered as a Plugin")
|
if (pluginYml == null) {
|
||||||
} else {
|
logger.info("plugin.yml not found in jar " + jar.name + ", it will not be consider as a Plugin")
|
||||||
val description =
|
} else {
|
||||||
PluginDescription.readFromContent(URL("jar:file:" + file.absoluteFile + "!/" + pluginYml.name).openConnection().inputStream.readAllBytes().encodeToString())
|
val url = URL("jar:file:" + file.absoluteFile + "!/" + pluginYml.name)
|
||||||
println(description)
|
val jarConnection: JarURLConnection = url
|
||||||
pluginsFound[description.name] = description
|
.openConnection() as JarURLConnection
|
||||||
pluginsLocation[description.name] = jar
|
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)
|
||||||
|
pluginsFound[description.pluginName] = description
|
||||||
|
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 {
|
|||||||
|
|
||||||
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 {
|
|||||||
|
|
||||||
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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user