Fix Settings

This commit is contained in:
Him188 2020-08-20 23:46:51 +08:00
parent b00b3d97f0
commit c13076eea0
10 changed files with 61 additions and 35 deletions

View File

@ -24,6 +24,7 @@ import net.mamoe.mirai.utils.MiraiLogger
import java.io.File import java.io.File
import java.util.concurrent.locks.ReentrantLock import java.util.concurrent.locks.ReentrantLock
import kotlin.annotation.AnnotationTarget.* import kotlin.annotation.AnnotationTarget.*
import kotlin.coroutines.CoroutineContext
/** /**
@ -44,6 +45,11 @@ public annotation class ConsoleFrontEndImplementation
*/ */
@ConsoleFrontEndImplementation @ConsoleFrontEndImplementation
public interface MiraiConsoleImplementation : CoroutineScope { public interface MiraiConsoleImplementation : CoroutineScope {
/**
* [MiraiConsole] [CoroutineScope.coroutineContext]
*/
public override val coroutineContext: CoroutineContext
/** /**
* Console 运行路径 * Console 运行路径
*/ */

View File

@ -107,7 +107,7 @@ public object BuiltInCommands {
private val closingLock = Mutex() private val closingLock = Mutex()
@Handler @Handler
public suspend fun CommandSender.handle(): Unit { public suspend fun CommandSender.handle() {
closingLock.withLock { closingLock.withLock {
sendMessage("Stopping mirai-console") sendMessage("Stopping mirai-console")
kotlin.runCatching { kotlin.runCatching {

View File

@ -13,6 +13,6 @@ import java.util.*
internal object MiraiConsoleBuildConstants { // auto-filled on build (task :mirai-console:fillBuildConstants) internal object MiraiConsoleBuildConstants { // auto-filled on build (task :mirai-console:fillBuildConstants)
@JvmStatic @JvmStatic
val buildDate: Date = Date(1596350800177L) // 2020-08-02 14:46:40 val buildDate: Date = Date(1597935352287L) // 2020-08-20 22:55:52
const val version: String = "1.0-M1" const val version: String = "1.0-M2-1"
} }

View File

@ -24,15 +24,20 @@ import kotlin.reflect.full.*
internal object CompositeCommandSubCommandAnnotationResolver : internal object CompositeCommandSubCommandAnnotationResolver :
AbstractReflectionCommand.SubCommandAnnotationResolver { AbstractReflectionCommand.SubCommandAnnotationResolver {
override fun hasAnnotation(function: KFunction<*>) = function.hasAnnotation<CompositeCommand.SubCommand>() override fun hasAnnotation(baseCommand: AbstractReflectionCommand, function: KFunction<*>) =
override fun getSubCommandNames(function: KFunction<*>): Array<out String> = function.hasAnnotation<CompositeCommand.SubCommand>()
override fun getSubCommandNames(baseCommand: AbstractReflectionCommand, function: KFunction<*>): Array<out String> =
function.findAnnotation<CompositeCommand.SubCommand>()!!.value function.findAnnotation<CompositeCommand.SubCommand>()!!.value
} }
internal object SimpleCommandSubCommandAnnotationResolver : internal object SimpleCommandSubCommandAnnotationResolver :
AbstractReflectionCommand.SubCommandAnnotationResolver { AbstractReflectionCommand.SubCommandAnnotationResolver {
override fun hasAnnotation(function: KFunction<*>) = function.hasAnnotation<SimpleCommand.Handler>() override fun hasAnnotation(baseCommand: AbstractReflectionCommand, function: KFunction<*>) =
override fun getSubCommandNames(function: KFunction<*>): Array<out String> = arrayOf("") function.hasAnnotation<SimpleCommand.Handler>()
override fun getSubCommandNames(baseCommand: AbstractReflectionCommand, function: KFunction<*>): Array<out String> =
baseCommand.names
} }
internal abstract class AbstractReflectionCommand @JvmOverloads constructor( internal abstract class AbstractReflectionCommand @JvmOverloads constructor(
@ -74,12 +79,12 @@ internal abstract class AbstractReflectionCommand @JvmOverloads constructor(
} }
interface SubCommandAnnotationResolver { interface SubCommandAnnotationResolver {
fun hasAnnotation(function: KFunction<*>): Boolean fun hasAnnotation(baseCommand: AbstractReflectionCommand, function: KFunction<*>): Boolean
fun getSubCommandNames(function: KFunction<*>): Array<out String> fun getSubCommandNames(baseCommand: AbstractReflectionCommand, function: KFunction<*>): Array<out String>
} }
internal val subCommands: Array<SubCommandDescriptor> by lazy { internal val subCommands: Array<SubCommandDescriptor> by lazy {
this::class.declaredFunctions.filter { subCommandAnnotationResolver.hasAnnotation(it) } this::class.declaredFunctions.filter { subCommandAnnotationResolver.hasAnnotation(this, it) }
.also { subCommandFunctions -> .also { subCommandFunctions ->
// overloading not yet supported // overloading not yet supported
val overloadFunction = subCommandFunctions.groupBy { it.name }.entries.firstOrNull { it.value.size > 1 } val overloadFunction = subCommandFunctions.groupBy { it.name }.entries.firstOrNull { it.value.size > 1 }
@ -278,7 +283,7 @@ internal fun AbstractReflectionCommand.createSubCommand(
} }
val commandName = val commandName =
subCommandAnnotationResolver.getSubCommandNames(function) subCommandAnnotationResolver.getSubCommandNames(this, function)
.let { namesFromAnnotation -> .let { namesFromAnnotation ->
if (namesFromAnnotation.isNotEmpty()) { if (namesFromAnnotation.isNotEmpty()) {
namesFromAnnotation namesFromAnnotation

View File

@ -11,10 +11,7 @@ package net.mamoe.mirai.console.internal.plugin
import kotlinx.atomicfu.AtomicLong import kotlinx.atomicfu.AtomicLong
import kotlinx.atomicfu.locks.withLock import kotlinx.atomicfu.locks.withLock
import kotlinx.coroutines.CoroutineExceptionHandler import kotlinx.coroutines.*
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.SupervisorJob
import net.mamoe.mirai.console.MiraiConsole import net.mamoe.mirai.console.MiraiConsole
import net.mamoe.mirai.console.plugin.Plugin import net.mamoe.mirai.console.plugin.Plugin
import net.mamoe.mirai.console.plugin.PluginManager import net.mamoe.mirai.console.plugin.PluginManager
@ -69,6 +66,7 @@ internal abstract class JvmPluginInternal(
internal fun internalOnDisable() { internal fun internalOnDisable() {
firstRun = false firstRun = false
this.onDisable() this.onDisable()
this.cancel(CancellationException("plugin disabled"))
} }
internal fun internalOnLoad() { internal fun internalOnLoad() {

View File

@ -12,6 +12,8 @@
package net.mamoe.mirai.console.internal.plugin package net.mamoe.mirai.console.internal.plugin
import kotlinx.atomicfu.locks.withLock import kotlinx.atomicfu.locks.withLock
import kotlinx.coroutines.InternalCoroutinesApi
import kotlinx.coroutines.Job
import net.mamoe.mirai.console.MiraiConsole import net.mamoe.mirai.console.MiraiConsole
import net.mamoe.mirai.console.internal.setting.cast import net.mamoe.mirai.console.internal.setting.cast
import net.mamoe.mirai.console.plugin.* import net.mamoe.mirai.console.plugin.*
@ -54,6 +56,12 @@ internal object PluginManagerImpl : PluginManager {
_pluginLoaders.remove(loader) _pluginLoaders.remove(loader)
} }
init {
@OptIn(InternalCoroutinesApi::class)
MiraiConsole.coroutineContext[Job]!!.invokeOnCompletion(true) {
plugins.forEach(Plugin::disable)
}
}
// region LOADING // region LOADING

View File

@ -10,10 +10,7 @@
package net.mamoe.mirai.console.internal.setting package net.mamoe.mirai.console.internal.setting
import kotlinx.atomicfu.atomic import kotlinx.atomicfu.atomic
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.*
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import net.mamoe.mirai.console.internal.command.qualifiedNameOrTip import net.mamoe.mirai.console.internal.command.qualifiedNameOrTip
import net.mamoe.mirai.console.internal.plugin.updateWhen import net.mamoe.mirai.console.internal.plugin.updateWhen
import net.mamoe.mirai.console.plugin.jvm.loadSetting import net.mamoe.mirai.console.plugin.jvm.loadSetting
@ -36,7 +33,10 @@ import kotlin.reflect.full.findAnnotation
* *
* @see loadSetting * @see loadSetting
*/ */
internal open class AutoSaveSetting(private val owner: AutoSaveSettingHolder) : internal open class AutoSaveSetting(
private val owner: AutoSaveSettingHolder,
internal val originSettingClass: KClass<out Setting>
) :
AbstractSetting() { AbstractSetting() {
private lateinit var storage: SettingStorage private lateinit var storage: SettingStorage
@ -54,7 +54,8 @@ internal open class AutoSaveSetting(private val owner: AutoSaveSettingHolder) :
internal var currentFirstStartTime = atomic(0L) internal var currentFirstStartTime = atomic(0L)
init { init {
owner.coroutineContext[Job]?.invokeOnCompletion { doSave() } @OptIn(InternalCoroutinesApi::class)
owner.coroutineContext[Job]?.invokeOnCompletion(true) { doSave() }
} }
private val updaterBlock: suspend CoroutineScope.() -> Unit = { private val updaterBlock: suspend CoroutineScope.() -> Unit = {
@ -125,10 +126,12 @@ internal class MemorySettingStorageImpl(
internal open class MultiFileSettingStorageImpl( internal open class MultiFileSettingStorageImpl(
public final override val directory: File public final override val directory: File
) : SettingStorage, MultiFileSettingStorage { ) : SettingStorage, MultiFileSettingStorage {
init {
directory.mkdir()
}
public override fun <T : Setting> load(holder: SettingHolder, settingClass: Class<T>): T = public override fun <T : Setting> load(holder: SettingHolder, settingClass: Class<T>): T =
with(settingClass.kotlin) { with(settingClass.kotlin) {
val file = getSettingFile(holder, this)
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
val instance = objectInstance ?: this.createInstanceOrNull() ?: kotlin.run { val instance = objectInstance ?: this.createInstanceOrNull() ?: kotlin.run {
require(settingClass == Setting::class.java) { require(settingClass == Setting::class.java) {
@ -136,15 +139,20 @@ internal open class MultiFileSettingStorageImpl(
"or has a constructor which either has no parameters or all parameters of which are optional" "or has a constructor which either has no parameters or all parameters of which are optional"
} }
if (holder is AutoSaveSettingHolder) { if (holder is AutoSaveSettingHolder) {
AutoSaveSetting(holder) as T? AutoSaveSetting(holder, this) as T?
} else null } else null
} ?: throw IllegalArgumentException( } ?: throw IllegalArgumentException(
"Cannot create Setting instance. Make sure 'holder' is a AutoSaveSettingHolder, " + "Cannot create Setting instance. Make sure 'holder' is a AutoSaveSettingHolder, " +
"or 'setting' is an object or has a constructor which either has no parameters or all parameters of which are optional" "or 'setting' is an object or has a constructor which either has no parameters or all parameters of which are optional"
) )
val file = getSettingFile(holder, this)
file.createNewFile() file.createNewFile()
check(file.exists() && file.isFile && file.canRead()) { "${file.absolutePath} cannot be read" } check(file.exists() && file.isFile && file.canRead()) { "${file.absolutePath} cannot be read" }
val text = file.readText()
if (text.isNotBlank()) {
Yaml.default.decodeFromString(instance.updaterSerializer, file.readText()) Yaml.default.decodeFromString(instance.updaterSerializer, file.readText())
}
instance instance
}.also { it.setStorage(this) } }.also { it.setStorage(this) }
@ -155,6 +163,7 @@ internal open class MultiFileSettingStorageImpl(
if (dir.isFile) { if (dir.isFile) {
error("Target directory ${dir.path} for holder $holder is occupied by a file therefore setting $qualifiedNameOrTip can't be saved.") error("Target directory ${dir.path} for holder $holder is occupied by a file therefore setting $qualifiedNameOrTip can't be saved.")
} }
dir.mkdir()
val file = File(directory, name) val file = File(directory, name)
if (file.isDirectory) { if (file.isDirectory) {
@ -164,8 +173,9 @@ internal open class MultiFileSettingStorageImpl(
} }
@ConsoleExperimentalAPI @ConsoleExperimentalAPI
public override fun store(holder: SettingHolder, setting: Setting): Unit = with(setting::class) { public override fun store(holder: SettingHolder, setting: Setting) {
val file = getSettingFile(holder, this) val file =
getSettingFile(holder, if (setting is AutoSaveSetting) setting.originSettingClass else setting::class)
if (file.exists() && file.isFile && file.canRead()) { if (file.exists() && file.isFile && file.canRead()) {
file.writeText(Yaml.default.encodeToString(setting.updaterSerializer, Unit)) file.writeText(Yaml.default.encodeToString(setting.updaterSerializer, Unit))

View File

@ -178,10 +178,10 @@ public interface AutoSaveSettingHolder : SettingHolder, CoroutineScope {
*/ */
@JvmDefault @JvmDefault
public override fun <T : Setting> newSettingInstance(type: KType): T { public override fun <T : Setting> newSettingInstance(type: KType): T {
val classifier = type.classifier?.cast<KClass<*>>()?.java val classifier = type.classifier?.cast<KClass<Setting>>()
require(classifier == Setting::class.java) { require(classifier != null && classifier.java == Setting::class.java) {
"Cannot create Setting instance. AutoSaveSettingHolder supports only Setting type." "Cannot create Setting instance. AutoSaveSettingHolder supports only Setting type."
} }
return AutoSaveSetting(this) as T // T is always Setting return AutoSaveSetting(this, classifier) as T // T is always Setting
} }
} }

View File

@ -46,7 +46,7 @@ internal fun startupConsoleThread() {
} }
} }
CoroutineScope(dispatch).launch { CoroutineScope(dispatch + SupervisorJob()).launch {
val consoleLogger = DefaultLogger("console") val consoleLogger = DefaultLogger("console")
while (isActive) { while (isActive) {
try { try {

View File

@ -48,14 +48,13 @@ class MiraiConsoleImplementationPure
@JvmOverloads constructor( @JvmOverloads constructor(
override val rootDir: File = File("."), override val rootDir: File = File("."),
override val builtInPluginLoaders: List<PluginLoader<*, *>> = Collections.unmodifiableList( override val builtInPluginLoaders: List<PluginLoader<*, *>> = Collections.unmodifiableList(
listOf( listOf(DeferredPluginLoader { JarPluginLoader })
DeferredPluginLoader { JarPluginLoader })
), ),
override val frontEnd: MiraiConsoleFrontEnd = MiraiConsoleFrontEndPure, override val frontEnd: MiraiConsoleFrontEnd = MiraiConsoleFrontEndPure,
override val mainLogger: MiraiLogger = frontEnd.loggerFor("main"), override val mainLogger: MiraiLogger = frontEnd.loggerFor("main"),
override val consoleCommandSender: ConsoleCommandSender = ConsoleCommandSenderImpl, override val consoleCommandSender: ConsoleCommandSender = ConsoleCommandSenderImpl,
override val settingStorageForJarPluginLoader: SettingStorage = MultiFileSettingStorage(rootDir), override val settingStorageForJarPluginLoader: SettingStorage = MultiFileSettingStorage(File(rootDir, "data")),
override val settingStorageForBuiltIns: SettingStorage = MultiFileSettingStorage(rootDir) override val settingStorageForBuiltIns: SettingStorage = MultiFileSettingStorage(File(rootDir, "data"))
) : MiraiConsoleImplementation, CoroutineScope by CoroutineScope(SupervisorJob()) { ) : MiraiConsoleImplementation, CoroutineScope by CoroutineScope(SupervisorJob()) {
init { init {
rootDir.mkdir() rootDir.mkdir()