mirror of
https://github.com/mamoe/mirai.git
synced 2025-02-02 04:30:25 +08:00
Merge remote-tracking branch 'origin/master'
This commit is contained in:
commit
3a2f0f3267
@ -1,16 +1,16 @@
|
|||||||
package net.mamoe.mirai.plugin
|
package net.mamoe.mirai.plugin
|
||||||
|
|
||||||
import net.mamoe.mirai.utils.DefaultLogger
|
import net.mamoe.mirai.utils.DefaultLogger
|
||||||
import java.io.BufferedReader
|
import net.mamoe.mirai.utils.io.encodeToString
|
||||||
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.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,38 +24,27 @@ 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 pluginName: String,
|
val name: String,
|
||||||
val pluginAuthor: String,
|
val author: String,
|
||||||
val pluginBasePath: String,
|
val basePath: String,
|
||||||
val pluginVersion: String,
|
val version: String,
|
||||||
val pluginInfo: String,
|
val info: 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: $pluginName\nauthor: $pluginAuthor\npath: $pluginBasePath\nver: $pluginVersion\ninfo: $pluginInfo\ndepends: $depends"
|
return "name: $name\nauthor: $author\npath: $basePath\nver: $version\ninfo: $info\ndepends: $depends"
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@ -69,11 +58,11 @@ class PluginDescription(
|
|||||||
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()
|
||||||
val lowercaseLine = line.toLowerCase()
|
val lowercaseLine = line.toLowerCase()
|
||||||
if(it.contains(":")) {
|
if (it.contains(":")) {
|
||||||
when{
|
when {
|
||||||
lowercaseLine.startsWith("name") -> {
|
lowercaseLine.startsWith("name") -> {
|
||||||
name = line.substringAfter(":").trim()
|
name = line.substringAfter(":").trim()
|
||||||
}
|
}
|
||||||
@ -83,9 +72,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("path") || lowercaseLine.startsWith(
|
lowercaseLine.startsWith("main") ||
|
||||||
"basepath"
|
lowercaseLine.startsWith("path") ||
|
||||||
) -> {
|
lowercaseLine.startsWith("basepath") -> {
|
||||||
basePath = line.substringAfter(":").trim()
|
basePath = line.substringAfter(":").trim()
|
||||||
}
|
}
|
||||||
lowercaseLine.startsWith("version") || lowercaseLine.startsWith("ver") -> {
|
lowercaseLine.startsWith("version") || lowercaseLine.startsWith("ver") -> {
|
||||||
@ -113,49 +102,37 @@ object PluginManager {
|
|||||||
private val nameToPluginBaseMap: MutableMap<String, PluginBase> = mutableMapOf()
|
private val nameToPluginBaseMap: MutableMap<String, PluginBase> = mutableMapOf()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 尝试加载全部插件
|
* 尝试加载全部插件
|
||||||
*/
|
*/
|
||||||
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, JarFile> = mutableMapOf()
|
||||||
|
|
||||||
File(pluginsPath).listFiles()?.forEach { file ->
|
File(pluginsPath).listFiles()?.forEach { file ->
|
||||||
if (file != null) {
|
if (file != null && file.extension == "jar") {
|
||||||
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
|
println(description)
|
||||||
.openConnection() as JarURLConnection
|
pluginsFound[description.name] = description
|
||||||
val inputStream: InputStream = jarConnection.getInputStream()
|
pluginsLocation[description.name] = jar
|
||||||
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] = jar
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun checkNoCircularDepends (target:PluginDescription, needDepends: List<String>,existDepends: MutableList<String>) {
|
fun checkNoCircularDepends(target: PluginDescription, needDepends: List<String>, existDepends: MutableList<String>) {
|
||||||
|
|
||||||
if (!target.noCircularDepend) {
|
if (!target.noCircularDepend) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
existDepends.add(target.pluginName)
|
existDepends.add(target.name)
|
||||||
|
|
||||||
if (needDepends.any { existDepends.contains(it) }) {
|
if (needDepends.any { existDepends.contains(it) }) {
|
||||||
target.noCircularDepend = false
|
target.noCircularDepend = false
|
||||||
@ -172,65 +149,58 @@ object PluginManager {
|
|||||||
|
|
||||||
|
|
||||||
pluginsFound.values.forEach {
|
pluginsFound.values.forEach {
|
||||||
checkNoCircularDepends(it,it.depends, mutableListOf())
|
checkNoCircularDepends(it, it.depends, mutableListOf())
|
||||||
}
|
}
|
||||||
|
|
||||||
//load
|
//load
|
||||||
|
|
||||||
|
|
||||||
fun loadPlugin(description: PluginDescription):Boolean{
|
fun loadPlugin(description: PluginDescription): Boolean {
|
||||||
if(!description.noCircularDepend){
|
if (!description.noCircularDepend) {
|
||||||
return false.also {
|
logger.error("Failed to load plugin " + description.name + " because it has circular dependency")
|
||||||
logger.error("Failed to load plugin " + description.pluginName + " because it has circular dependency")
|
return false
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//load depends first
|
//load depends first
|
||||||
description.depends.forEach{
|
description.depends.forEach { dependentName ->
|
||||||
if(!pluginsFound.containsKey(it)){
|
val dependent = pluginsFound[dependentName]
|
||||||
return false.also { _ ->
|
if (dependent == null) {
|
||||||
logger.error("Failed to load plugin " + description.pluginName + " because it need " + it + " as dependency")
|
logger.error("Failed to load plugin " + description.name + " because it need " + dependentName + " as dependency")
|
||||||
}
|
return false
|
||||||
}
|
}
|
||||||
val depend = pluginsFound[it]!!
|
|
||||||
//还没有加载
|
//还没有加载
|
||||||
if(!depend.loaded) {
|
if (!dependent.loaded && !loadPlugin(dependent)) {
|
||||||
if (!loadPlugin(pluginsFound[it]!!)) {
|
logger.error("Failed to load plugin " + description.name + " because " + dependentName + " as dependency failed to load")
|
||||||
return false.also { _ ->
|
return false
|
||||||
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.pluginName)
|
logger.info("loading plugin " + description.name)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.javaClass.classLoader.loadClass(description.pluginBasePath)
|
this.javaClass.classLoader.loadClass(description.basePath)
|
||||||
return try {
|
return try {
|
||||||
val subClass = javaClass.asSubclass(PluginBase::class.java)
|
val subClass = javaClass.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.pluginName)
|
logger.info("successfully loaded plugin " + description.name)
|
||||||
logger.info(description.pluginInfo)
|
logger.info(description.info)
|
||||||
|
|
||||||
nameToPluginBaseMap[description.pluginName] = plugin
|
nameToPluginBaseMap[description.name] = plugin
|
||||||
plugin.init(description)
|
plugin.init(description)
|
||||||
|
|
||||||
true
|
true
|
||||||
} catch (e: ClassCastException) {
|
} catch (e: ClassCastException) {
|
||||||
false.also {
|
logger.error("failed to load plugin " + description.name + " , Main class does not extends PluginBase ")
|
||||||
logger.error("failed to load plugin " + description.pluginName + " , Main class does not extends PluginBase ")
|
false
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch (e: ClassNotFoundException) {
|
} catch (e: ClassNotFoundException) {
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
return false.also {
|
logger.error("failed to load plugin " + description.name + " , Main class not found under " + description.basePath)
|
||||||
logger.error("failed to load plugin " + description.pluginName + " , Main class not found under " + description.pluginBasePath)
|
return false
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -240,8 +210,6 @@ object PluginManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user