mirror of
https://github.com/mamoe/mirai.git
synced 2025-01-25 15:40:28 +08:00
Fix delegation in Values;
Rename IllegalMiraiConsoleImplementationError to MalformedMiraiConsoleImplementationError; Support Managers command Misc improvements
This commit is contained in:
parent
71b1464e25
commit
a6aa160a97
@ -19,6 +19,7 @@ import net.mamoe.mirai.Bot
|
||||
import net.mamoe.mirai.console.MiraiConsole.INSTANCE
|
||||
import net.mamoe.mirai.console.MiraiConsoleImplementation.Companion.start
|
||||
import net.mamoe.mirai.console.internal.MiraiConsoleImplementationBridge
|
||||
import net.mamoe.mirai.console.internal.util.childScopeContext
|
||||
import net.mamoe.mirai.console.plugin.PluginLoader
|
||||
import net.mamoe.mirai.console.plugin.center.PluginCenter
|
||||
import net.mamoe.mirai.console.plugin.jvm.JarPluginLoader
|
||||
@ -87,7 +88,7 @@ public interface MiraiConsole : CoroutineScope {
|
||||
*/ // MiraiConsole.INSTANCE.getJob()
|
||||
public val job: Job
|
||||
get() = MiraiConsole.coroutineContext[Job]
|
||||
?: throw IllegalMiraiConsoleImplementationError("Internal error: Job not found in MiraiConsole.coroutineContext")
|
||||
?: throw MalformedMiraiConsoleImplementationError("Internal error: Job not found in MiraiConsole.coroutineContext")
|
||||
|
||||
/**
|
||||
* 添加一个 [Bot] 实例到全局 Bot 列表, 但不登录.
|
||||
@ -102,6 +103,7 @@ public interface MiraiConsole : CoroutineScope {
|
||||
Bot(id, password) {
|
||||
fileBasedDeviceInfo()
|
||||
redirectNetworkLogToDirectory()
|
||||
parentCoroutineContext = MiraiConsole.childScopeContext()
|
||||
|
||||
this.loginSolver = MiraiConsoleImplementationBridge.createLoginSolver(id, this)
|
||||
configuration()
|
||||
@ -119,8 +121,10 @@ public val MiraiConsole.rootDir: File get() = rootPath.toFile()
|
||||
*
|
||||
* @see MiraiConsoleImplementation.start
|
||||
*/
|
||||
public class IllegalMiraiConsoleImplementationError @JvmOverloads constructor(
|
||||
public override val message: String? = null,
|
||||
public override val cause: Throwable? = null
|
||||
) : Error()
|
||||
public class MalformedMiraiConsoleImplementationError : Error {
|
||||
public constructor() : super()
|
||||
public constructor(message: String?) : super(message)
|
||||
public constructor(message: String?, cause: Throwable?) : super(message, cause)
|
||||
public constructor(cause: Throwable?) : super(cause)
|
||||
}
|
||||
|
||||
|
@ -81,6 +81,7 @@ public interface MiraiConsoleImplementation : CoroutineScope {
|
||||
public val dataStorageForJarPluginLoader: PluginDataStorage
|
||||
public val configStorageForJarPluginLoader: PluginDataStorage
|
||||
public val dataStorageForBuiltIns: PluginDataStorage
|
||||
public val configStorageForBuiltIns: PluginDataStorage
|
||||
|
||||
/**
|
||||
* @see ConsoleInput 的实现
|
||||
@ -111,7 +112,7 @@ public interface MiraiConsoleImplementation : CoroutineScope {
|
||||
/** 由前端调用, 初始化 [MiraiConsole] 实例, 并启动 */
|
||||
@JvmStatic
|
||||
@ConsoleFrontEndImplementation
|
||||
@Throws(IllegalMiraiConsoleImplementationError::class)
|
||||
@Throws(MalformedMiraiConsoleImplementationError::class)
|
||||
public fun MiraiConsoleImplementation.start(): Unit = initLock.withLock {
|
||||
this@Companion.instance = this
|
||||
MiraiConsoleImplementationBridge.doStart()
|
||||
|
@ -14,13 +14,20 @@ import kotlinx.coroutines.cancelAndJoin
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import net.mamoe.mirai.Bot
|
||||
import net.mamoe.mirai.alsoLogin
|
||||
import net.mamoe.mirai.console.MiraiConsole
|
||||
import net.mamoe.mirai.console.command.CommandManager.INSTANCE.register
|
||||
import net.mamoe.mirai.console.internal.command.CommandManagerImpl
|
||||
import net.mamoe.mirai.console.internal.command.CommandManagerImpl.allRegisteredCommands
|
||||
import net.mamoe.mirai.console.internal.command.qualifiedNameOrTip
|
||||
import net.mamoe.mirai.console.util.BotManager.INSTANCE.addManager
|
||||
import net.mamoe.mirai.console.util.BotManager.INSTANCE.managers
|
||||
import net.mamoe.mirai.console.util.BotManager.INSTANCE.removeManager
|
||||
import net.mamoe.mirai.console.util.ConsoleExperimentalAPI
|
||||
import net.mamoe.mirai.console.util.ConsoleInternalAPI
|
||||
import net.mamoe.mirai.contact.*
|
||||
import net.mamoe.mirai.getFriendOrNull
|
||||
import net.mamoe.mirai.message.nextMessageOrNull
|
||||
import net.mamoe.mirai.utils.secondsToMillis
|
||||
import kotlin.concurrent.thread
|
||||
@ -51,9 +58,36 @@ public object BuiltInCommands {
|
||||
}
|
||||
}
|
||||
|
||||
public object Managers : CompositeCommand(
|
||||
ConsoleCommandOwner, "managers",
|
||||
description = "Manage the managers for each bot",
|
||||
permission = CommandPermission.Console or CommandPermission.Manager
|
||||
), BuiltInCommand {
|
||||
@Permission(CommandPermission.Console::class)
|
||||
@SubCommand
|
||||
public suspend fun CommandSender.add(target: User) {
|
||||
target.bot.addManager(target.id)
|
||||
sendMessage("已成功添加 ${target.render()} 为 ${target.bot.render()} 的管理员")
|
||||
}
|
||||
|
||||
@Permission(CommandPermission.Console::class)
|
||||
@SubCommand
|
||||
public suspend fun CommandSender.remove(target: User) {
|
||||
target.bot.removeManager(target.id)
|
||||
sendMessage("已成功取消 ${target.render()} 对 ${target.bot.render()} 的管理员权限")
|
||||
}
|
||||
|
||||
@SubCommand
|
||||
public suspend fun CommandSender.list(bot: Bot) {
|
||||
sendMessage("$bot 的管理员列表:\n" + bot.managers.joinToString("\n") {
|
||||
bot.getFriendOrNull(it)?.render() ?: it.toString()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
public object Help : SimpleCommand(
|
||||
ConsoleCommandOwner, "help",
|
||||
description = "Gets help about the console."
|
||||
description = "Command list"
|
||||
), BuiltInCommand {
|
||||
@Handler
|
||||
public suspend fun CommandSender.handle() {
|
||||
@ -63,15 +97,16 @@ public object BuiltInCommands {
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
Runtime.getRuntime().addShutdownHook(thread(false) {
|
||||
MiraiConsole.cancel()
|
||||
})
|
||||
}
|
||||
|
||||
public object Stop : SimpleCommand(
|
||||
ConsoleCommandOwner, "stop", "shutdown", "exit",
|
||||
description = "Stop the whole world."
|
||||
), BuiltInCommand {
|
||||
init {
|
||||
Runtime.getRuntime().addShutdownHook(thread(false) {
|
||||
MiraiConsole.cancel()
|
||||
})
|
||||
}
|
||||
|
||||
private val closingLock = Mutex()
|
||||
|
||||
@ -122,6 +157,16 @@ public object BuiltInCommands {
|
||||
}
|
||||
}
|
||||
|
||||
internal fun ContactOrBot.render(): String {
|
||||
return when (this) {
|
||||
is Bot -> "Bot $nick($id)"
|
||||
is Group -> "Group $name($id)"
|
||||
is Friend -> "Friend $nick($id)"
|
||||
is Member -> "Friend $nameCardOrNick($id)"
|
||||
else -> error("Illegal type for ContactOrBot: ${this::class.qualifiedNameOrTip}")
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
/**
|
||||
|
@ -16,6 +16,7 @@ import kotlinx.coroutines.*
|
||||
import net.mamoe.mirai.console.internal.plugin.updateWhen
|
||||
import net.mamoe.mirai.console.util.ConsoleExperimentalAPI
|
||||
import net.mamoe.mirai.console.util.ConsoleInternalAPI
|
||||
import net.mamoe.mirai.utils.DefaultLogger
|
||||
import net.mamoe.mirai.utils.currentTimeMillis
|
||||
|
||||
/**
|
||||
@ -39,7 +40,11 @@ public open class AutoSavePluginData private constructor(
|
||||
|
||||
override fun onStored(owner: PluginDataHolder, storage: PluginDataStorage) {
|
||||
check(owner is AutoSavePluginDataHolder) { "owner must be AutoSavePluginDataHolder for AutoSavePluginData" }
|
||||
check(!this::storage_.isInitialized) { "storage is already initialized" }
|
||||
|
||||
if (this::storage_.isInitialized) {
|
||||
check(storage == this.storage_) { "AutoSavePluginData is already initialized with one storage and cannot be reinitialized with another." }
|
||||
}
|
||||
|
||||
this.storage_ = storage
|
||||
this.owner_ = owner
|
||||
|
||||
@ -90,10 +95,16 @@ public open class AutoSavePluginData private constructor(
|
||||
@Suppress("RedundantVisibilityModifier")
|
||||
@ConsoleInternalAPI
|
||||
public final override fun onValueChanged(value: Value<*>) {
|
||||
debuggingLogger1.error("onValueChanged: $value")
|
||||
if (::owner_.isInitialized) {
|
||||
lastAutoSaveJob_ = owner_.launch(block = updaterBlock)
|
||||
}
|
||||
}
|
||||
|
||||
private fun doSave() = storage_.store(owner_, this)
|
||||
}
|
||||
private fun doSave() {
|
||||
debuggingLogger1.error("doSave: ${this::class.qualifiedName}")
|
||||
storage_.store(owner_, this)
|
||||
}
|
||||
}
|
||||
|
||||
internal val debuggingLogger1 = DefaultLogger("debug")
|
@ -76,7 +76,7 @@ import kotlin.reflect.full.findAnnotation
|
||||
* @see JvmPlugin.reloadPluginData 通过 [JvmPlugin] 获取指定 [PluginData] 实例.
|
||||
* @see PluginDataStorage [PluginData] 存储仓库
|
||||
*/
|
||||
public interface PluginData {
|
||||
public interface PluginData : PluginDataExtensions {
|
||||
/**
|
||||
* 添加了追踪的 [ValueNode] 列表 (即使用 `by value()` 委托的属性), 即通过 `by value` 初始化的属性列表.
|
||||
*
|
||||
|
@ -0,0 +1,86 @@
|
||||
package net.mamoe.mirai.console.data
|
||||
|
||||
import net.mamoe.mirai.console.internal.data.CompositeMapValueImpl
|
||||
import net.mamoe.mirai.console.internal.data.castOrInternalError
|
||||
import net.mamoe.mirai.console.internal.data.createCompositeMapValueImpl
|
||||
|
||||
@Suppress("INAPPLICABLE_JVM_NAME", "UNCHECKED_CAST")
|
||||
public interface PluginDataExtensions {
|
||||
|
||||
@JvmName("withDefaultImmutable")
|
||||
public fun <V, K> SerializerAwareValue<Map<K, V>>.withDefault(defaultValueComputer: (K) -> V): SerializerAwareValue<Map<K, V>> {
|
||||
return (this as SerializerAwareValue<MutableMap<K, V>>).withDefault(defaultValueComputer) as SerializerAwareValue<Map<K, V>>
|
||||
}
|
||||
|
||||
@JvmName("withDefaultImmutableMap")
|
||||
public fun <M : Map<K, V>, V : Map<*, *>, K> SerializerAwareValue<M>.withEmptyDefault(): SerializerAwareValue<M> {
|
||||
return this.withDefault { LinkedHashMap<Any?, Any?>() as V }
|
||||
}
|
||||
|
||||
@JvmName("withDefaultImmutableSet")
|
||||
public fun <M : Map<K, V>, V : Set<*>, K> SerializerAwareValue<M>.withEmptyDefault(): SerializerAwareValue<M> {
|
||||
return this.withDefault { LinkedHashSet<Any?>() as V }
|
||||
}
|
||||
|
||||
@JvmName("withDefaultImmutableList")
|
||||
public fun <M : Map<K, V>, V : List<*>, K> SerializerAwareValue<M>.withEmptyDefault(): SerializerAwareValue<M> {
|
||||
return this.withDefault { ArrayList<Any?>() as V }
|
||||
}
|
||||
|
||||
public fun <M : Map<K, V>, V, K> SerializerAwareValue<M>.withDefault(defaultValueComputer: (K) -> V): SerializerAwareValue<M> {
|
||||
val pluginData = this@PluginDataExtensions.castOrInternalError<PluginData>()
|
||||
|
||||
|
||||
val origin = (this as SerializableValue<M>).delegate.castOrInternalError<CompositeMapValueImpl<K, V>>()
|
||||
|
||||
return SerializableValue(
|
||||
object : CompositeMapValue<K, V> {
|
||||
private val instance = object : MutableMap<K, V> {
|
||||
override val size: Int get() = origin.value.size
|
||||
override fun containsKey(key: K): Boolean = origin.value.containsKey(key)
|
||||
override fun containsValue(value: V): Boolean = origin.value.containsValue(value)
|
||||
override fun isEmpty(): Boolean = origin.value.isEmpty()
|
||||
override val entries: MutableSet<MutableMap.MutableEntry<K, V>> get() = origin.value.entries as MutableSet<MutableMap.MutableEntry<K, V>>
|
||||
override val keys: MutableSet<K> get() = origin.value.keys as MutableSet<K>
|
||||
override val values: MutableCollection<V> get() = origin.value.values as MutableCollection<V>
|
||||
override fun clear() = (origin.value as MutableMap<K, V>).clear()
|
||||
override fun putAll(from: Map<out K, V>) = (origin.value as MutableMap<K, V>).putAll(from)
|
||||
override fun remove(key: K): V? = (origin.value as MutableMap<K, V>).remove(key)
|
||||
override fun put(key: K, value: V): V? = (origin.value as MutableMap<K, V>).put(key, value)
|
||||
|
||||
override fun get(key: K): V? {
|
||||
// the only difference
|
||||
val result = origin.value[key]
|
||||
if (result != null) {
|
||||
return result
|
||||
}
|
||||
put(key, defaultValueComputer(key))
|
||||
return origin.value[key]
|
||||
}
|
||||
}
|
||||
|
||||
override var value: Map<K, V>
|
||||
get() = instance
|
||||
set(value) {
|
||||
origin.value = value
|
||||
}
|
||||
} as Value<M>,
|
||||
this.serializer
|
||||
)
|
||||
return pluginData.createCompositeMapValueImpl(
|
||||
kToValue = origin.kToValue,
|
||||
vToValue = origin.vToValue,
|
||||
applyToShadowedMap = { theMap ->
|
||||
object : MutableMap<K, V> by theMap {
|
||||
override fun get(key: K): V? {
|
||||
val result = theMap[key]
|
||||
if (result != null) return result
|
||||
theMap[key] = defaultValueComputer(key)
|
||||
return theMap[key]
|
||||
}
|
||||
}
|
||||
}
|
||||
).let { SerializableValue(it, serializer) } as SerializerAwareValue<M>
|
||||
}
|
||||
|
||||
}
|
@ -40,7 +40,7 @@ public interface Value<T> {
|
||||
* 可被序列化的 [Value].
|
||||
*/
|
||||
public class SerializableValue<T>(
|
||||
private val delegate: Value<T>,
|
||||
@JvmField internal val delegate: Value<T>,
|
||||
/**
|
||||
* 用于更新和保存 [delegate] 的序列化器
|
||||
*/
|
||||
@ -56,7 +56,9 @@ public class SerializableValue<T>(
|
||||
): SerializableValue<T> {
|
||||
return SerializableValue(
|
||||
this,
|
||||
serializer.map(serializer = { this.value }, deserializer = { this.setValueBySerializer(it) })
|
||||
serializer.map(serializer = {
|
||||
this.value
|
||||
}, deserializer = { this.setValueBySerializer(it) })
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ import com.vdurmont.semver4j.Semver
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Job
|
||||
import net.mamoe.mirai.Bot
|
||||
import net.mamoe.mirai.console.IllegalMiraiConsoleImplementationError
|
||||
import net.mamoe.mirai.console.MalformedMiraiConsoleImplementationError
|
||||
import net.mamoe.mirai.console.MiraiConsole
|
||||
import net.mamoe.mirai.console.MiraiConsoleFrontEndDescription
|
||||
import net.mamoe.mirai.console.MiraiConsoleImplementation
|
||||
@ -26,7 +26,7 @@ import net.mamoe.mirai.console.data.PluginDataStorage
|
||||
import net.mamoe.mirai.console.internal.command.CommandManagerImpl
|
||||
import net.mamoe.mirai.console.internal.plugin.CuiPluginCenter
|
||||
import net.mamoe.mirai.console.internal.plugin.PluginManagerImpl
|
||||
import net.mamoe.mirai.console.internal.util.ConsoleBuiltInPluginDataStorage
|
||||
import net.mamoe.mirai.console.internal.util.ConsoleDataScope
|
||||
import net.mamoe.mirai.console.plugin.PluginLoader
|
||||
import net.mamoe.mirai.console.plugin.PluginManager
|
||||
import net.mamoe.mirai.console.plugin.center.PluginCenter
|
||||
@ -62,6 +62,7 @@ internal object MiraiConsoleImplementationBridge : CoroutineScope, MiraiConsoleI
|
||||
override val dataStorageForJarPluginLoader: PluginDataStorage by instance::dataStorageForJarPluginLoader
|
||||
override val configStorageForJarPluginLoader: PluginDataStorage by instance::configStorageForJarPluginLoader
|
||||
override val dataStorageForBuiltIns: PluginDataStorage by instance::dataStorageForBuiltIns
|
||||
override val configStorageForBuiltIns: PluginDataStorage by instance::configStorageForBuiltIns
|
||||
override val consoleInput: ConsoleInput by instance::consoleInput
|
||||
|
||||
override fun createLoginSolver(requesterBot: Long, configuration: BotConfiguration): LoginSolver =
|
||||
@ -83,15 +84,18 @@ internal object MiraiConsoleImplementationBridge : CoroutineScope, MiraiConsoleI
|
||||
mainLogger.info { frontEndDescription.render() }
|
||||
|
||||
if (coroutineContext[Job] == null) {
|
||||
throw IllegalMiraiConsoleImplementationError("The coroutineContext given to MiraiConsole must have a Job in it.")
|
||||
throw MalformedMiraiConsoleImplementationError("The coroutineContext given to MiraiConsole must have a Job in it.")
|
||||
}
|
||||
|
||||
MiraiConsole.job.invokeOnCompletion {
|
||||
Bot.botInstances.forEach { kotlin.runCatching { it.close() }.exceptionOrNull()?.let(mainLogger::error) }
|
||||
}
|
||||
|
||||
mainLogger.info { "Reloading configurations..." }
|
||||
ConsoleDataScope.reloadAll()
|
||||
|
||||
BuiltInCommands.registerAll()
|
||||
mainLogger.info { "Preparing built-in commands: ${BuiltInCommands.all.joinToString { it.primaryName }}" }
|
||||
mainLogger.info { "Prepared built-in commands: ${BuiltInCommands.all.joinToString { it.primaryName }}" }
|
||||
CommandManagerImpl.commandListener // start
|
||||
|
||||
mainLogger.info { "Loading plugins..." }
|
||||
@ -99,7 +103,6 @@ internal object MiraiConsoleImplementationBridge : CoroutineScope, MiraiConsoleI
|
||||
mainLogger.info { "${PluginManager.plugins.size} plugin(s) loaded." }
|
||||
mainLogger.info { "mirai-console started successfully." }
|
||||
|
||||
ConsoleBuiltInPluginDataStorage // init
|
||||
// Only for initialize
|
||||
}
|
||||
}
|
@ -130,11 +130,11 @@ internal abstract class AbstractReflectionCommand @JvmOverloads constructor(
|
||||
removeSubName: Boolean
|
||||
) {
|
||||
val args = parseArgs(sender, argsWithSubCommandNameNotRemoved, if (removeSubName) names.size else 0)
|
||||
if (args == null || !onCommand(
|
||||
sender,
|
||||
args
|
||||
)
|
||||
) {
|
||||
if (!this.permission.testPermission(sender)) {
|
||||
sender.sendMessage(usage) // TODO: 2020/8/26 #127
|
||||
return
|
||||
}
|
||||
if (args == null || !onCommand(sender, args)) {
|
||||
sender.sendMessage(usage)
|
||||
}
|
||||
}
|
||||
|
@ -110,9 +110,12 @@ internal abstract class CompositeListValueImpl<T>(
|
||||
// workaround to a type inference bug
|
||||
internal fun <K, V> PluginData.createCompositeMapValueImpl(
|
||||
kToValue: (K) -> Value<K>,
|
||||
vToValue: (V) -> Value<V>
|
||||
vToValue: (V) -> Value<V>,
|
||||
valueToK: (Value<K>) -> K = Value<K>::value,
|
||||
valueToV: (Value<V>) -> V = Value<V>::value,
|
||||
applyToShadowedMap: ((MutableMap<K, V>) -> (MutableMap<K, V>))? = null
|
||||
): CompositeMapValueImpl<K, V> {
|
||||
return object : CompositeMapValueImpl<K, V>(kToValue, vToValue) {
|
||||
return object : CompositeMapValueImpl<K, V>(kToValue, vToValue, valueToK, valueToV, applyToShadowedMap) {
|
||||
override fun onChanged() = this@createCompositeMapValueImpl.onValueChanged(this)
|
||||
}
|
||||
}
|
||||
@ -120,13 +123,20 @@ internal fun <K, V> PluginData.createCompositeMapValueImpl(
|
||||
// TODO: 2020/6/24 在一个 Value 被删除后停止追踪其更新.
|
||||
|
||||
internal abstract class CompositeMapValueImpl<K, V>(
|
||||
kToValue: (K) -> Value<K>, // should override onChanged
|
||||
vToValue: (V) -> Value<V> // should override onChanged
|
||||
@JvmField internal val kToValue: (K) -> Value<K>, // should override onChanged
|
||||
@JvmField internal val vToValue: (V) -> Value<V>, // should override onChanged
|
||||
@JvmField internal val valueToK: (Value<K>) -> K = Value<K>::value,
|
||||
@JvmField internal val valueToV: (Value<V>) -> V = Value<V>::value,
|
||||
applyToShadowedMap: ((MutableMap<K, V>) -> (MutableMap<K, V>))? = null
|
||||
) : CompositeMapValue<K, V>, AbstractValueImpl<Map<K, V>>() {
|
||||
private val internalList: MutableMap<Value<K>, Value<V>> = mutableMapOf()
|
||||
@JvmField
|
||||
internal val internalList: MutableMap<Value<K>, Value<V>> = mutableMapOf()
|
||||
|
||||
private var _value: MutableMap<K, V> =
|
||||
internalList.shadowMap({ it.value }, kToValue, { it.value }, vToValue).observable { onChanged() }
|
||||
internalList.shadowMap(valueToK, kToValue, valueToV, vToValue).let {
|
||||
applyToShadowedMap?.invoke(it) ?: it
|
||||
}.observable { onChanged() }
|
||||
|
||||
override var value: Map<K, V>
|
||||
get() = _value
|
||||
set(v) {
|
||||
|
@ -18,73 +18,94 @@ import kotlin.reflect.KClass
|
||||
|
||||
// TODO: 2020/6/24 优化性能: 引入一个 comparator 之类来替代将 Int 包装为 Value<Int> 后进行 containsKey 比较的方法
|
||||
|
||||
internal inline fun <K, V, KR, VR> MutableMap<K, V>.shadowMap(
|
||||
crossinline kTransform: (K) -> KR,
|
||||
crossinline kTransformBack: (KR) -> K,
|
||||
crossinline vTransform: (V) -> VR,
|
||||
crossinline vTransformBack: (VR) -> V
|
||||
): MutableMap<KR, VR> {
|
||||
return object : MutableMap<KR, VR> {
|
||||
override val size: Int get() = this@shadowMap.size
|
||||
override fun containsKey(key: KR): Boolean = this@shadowMap.containsKey(key.let(kTransformBack))
|
||||
override fun containsValue(value: VR): Boolean = this@shadowMap.containsValue(value.let(vTransformBack))
|
||||
override fun get(key: KR): VR? = this@shadowMap[key.let(kTransformBack)]?.let(vTransform)
|
||||
override fun isEmpty(): Boolean = this@shadowMap.isEmpty()
|
||||
|
||||
override val entries: MutableSet<MutableMap.MutableEntry<KR, VR>>
|
||||
get() = this@shadowMap.entries.shadowMap(
|
||||
transform = { entry: MutableMap.MutableEntry<K, V> ->
|
||||
object : MutableMap.MutableEntry<KR, VR> {
|
||||
override val key: KR get() = entry.key.let(kTransform)
|
||||
override val value: VR get() = entry.value.let(vTransform)
|
||||
override fun setValue(newValue: VR): VR =
|
||||
entry.setValue(newValue.let(vTransformBack)).let(vTransform)
|
||||
internal open class ShadowMap<K, V, KR, VR>(
|
||||
private val originMap: MutableMap<K, V>,
|
||||
private val kTransform: (K) -> KR,
|
||||
private val kTransformBack: (KR) -> K,
|
||||
private val vTransform: (V) -> VR,
|
||||
private val vTransformBack: (VR) -> V
|
||||
) : MutableMap<KR, VR> {
|
||||
override val size: Int get() = originMap.size
|
||||
override fun containsKey(key: KR): Boolean = originMap.containsKey(key.let(kTransformBack))
|
||||
override fun containsValue(value: VR): Boolean = originMap.containsValue(value.let(vTransformBack))
|
||||
override fun get(key: KR): VR? = originMap[key.let(kTransformBack)]?.let(vTransform)
|
||||
override fun isEmpty(): Boolean = originMap.isEmpty()
|
||||
|
||||
override fun hashCode(): Int = 17 * 31 + (key?.hashCode() ?: 0) + (value?.hashCode() ?: 0)
|
||||
override fun toString(): String = "$key=$value"
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other == null || other !is Map.Entry<*, *>) return false
|
||||
return other.key == key && other.value == value
|
||||
}
|
||||
}
|
||||
} as ((MutableMap.MutableEntry<K, V>) -> MutableMap.MutableEntry<KR, VR>), // type inference bug
|
||||
transformBack = { entry ->
|
||||
object : MutableMap.MutableEntry<K, V> {
|
||||
override val key: K get() = entry.key.let(kTransformBack)
|
||||
override val value: V get() = entry.value.let(vTransformBack)
|
||||
override fun setValue(newValue: V): V =
|
||||
entry.setValue(newValue.let(vTransform)).let(vTransformBack)
|
||||
override val entries: MutableSet<MutableMap.MutableEntry<KR, VR>>
|
||||
get() = originMap.entries.shadowMap(
|
||||
transform = { entry: MutableMap.MutableEntry<K, V> ->
|
||||
object : MutableMap.MutableEntry<KR, VR> {
|
||||
override val key: KR get() = entry.key.let(kTransform)
|
||||
override val value: VR get() = entry.value.let(vTransform)
|
||||
override fun setValue(newValue: VR): VR =
|
||||
entry.setValue(newValue.let(vTransformBack)).let(vTransform)
|
||||
|
||||
override fun hashCode(): Int = 17 * 31 + (key?.hashCode() ?: 0) + (value?.hashCode() ?: 0)
|
||||
override fun toString(): String = "$key=$value"
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other == null || other !is Map.Entry<*, *>) return false
|
||||
return other.key == key && other.value == value
|
||||
}
|
||||
override fun hashCode(): Int = 17 * 31 + (key?.hashCode() ?: 0) + (value?.hashCode() ?: 0)
|
||||
override fun toString(): String = "$key=$value"
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other == null || other !is Map.Entry<*, *>) return false
|
||||
return other.key == key && other.value == value
|
||||
}
|
||||
}
|
||||
)
|
||||
override val keys: MutableSet<KR>
|
||||
get() = this@shadowMap.keys.shadowMap(kTransform, kTransformBack)
|
||||
override val values: MutableCollection<VR>
|
||||
get() = this@shadowMap.values.shadowMap(vTransform, vTransformBack)
|
||||
} as ((MutableMap.MutableEntry<K, V>) -> MutableMap.MutableEntry<KR, VR>), // type inference bug
|
||||
transformBack = { entry ->
|
||||
object : MutableMap.MutableEntry<K, V> {
|
||||
override val key: K get() = entry.key.let(kTransformBack)
|
||||
override val value: V get() = entry.value.let(vTransformBack)
|
||||
override fun setValue(newValue: V): V =
|
||||
entry.setValue(newValue.let(vTransform)).let(vTransformBack)
|
||||
|
||||
override fun clear() = this@shadowMap.clear()
|
||||
override fun put(key: KR, value: VR): VR? =
|
||||
this@shadowMap.put(key.let(kTransformBack), value.let(vTransformBack))?.let(vTransform)
|
||||
|
||||
override fun putAll(from: Map<out KR, VR>) {
|
||||
from.forEach { (kr, vr) ->
|
||||
this@shadowMap[kr.let(kTransformBack)] = vr.let(vTransformBack)
|
||||
override fun hashCode(): Int = 17 * 31 + (key?.hashCode() ?: 0) + (value?.hashCode() ?: 0)
|
||||
override fun toString(): String = "$key=$value"
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other == null || other !is Map.Entry<*, *>) return false
|
||||
return other.key == key && other.value == value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
override val keys: MutableSet<KR>
|
||||
get() = originMap.keys.shadowMap(kTransform, kTransformBack)
|
||||
override val values: MutableCollection<VR>
|
||||
get() = originMap.values.shadowMap(vTransform, vTransformBack)
|
||||
|
||||
override fun remove(key: KR): VR? = this@shadowMap.remove(key.let(kTransformBack))?.let(vTransform)
|
||||
override fun toString(): String = this@shadowMap.toString()
|
||||
override fun hashCode(): Int = this@shadowMap.hashCode()
|
||||
override fun clear() = originMap.clear()
|
||||
override fun put(key: KR, value: VR): VR? =
|
||||
originMap.put(key.let(kTransformBack), value.let(vTransformBack))?.let(vTransform)
|
||||
|
||||
override fun putAll(from: Map<out KR, VR>) {
|
||||
from.forEach { (kr, vr) ->
|
||||
originMap[kr.let(kTransformBack)] = vr.let(vTransformBack)
|
||||
}
|
||||
}
|
||||
|
||||
override fun remove(key: KR): VR? = originMap.remove(key.let(kTransformBack))?.let(vTransform)
|
||||
override fun toString(): String = originMap.toString()
|
||||
override fun hashCode(): Int = originMap.hashCode()
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
|
||||
other as ShadowMap<*, *, *, *>
|
||||
|
||||
if (originMap != other.originMap) return false
|
||||
if (kTransform != other.kTransform) return false
|
||||
if (kTransformBack != other.kTransformBack) return false
|
||||
if (vTransform != other.vTransform) return false
|
||||
if (vTransformBack != other.vTransformBack) return false
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
internal fun <K, V, KR, VR> MutableMap<K, V>.shadowMap(
|
||||
kTransform: (K) -> KR,
|
||||
kTransformBack: (KR) -> K,
|
||||
vTransform: (V) -> VR,
|
||||
vTransformBack: (VR) -> V
|
||||
): MutableMap<KR, VR> = ShadowMap(this, kTransform, kTransformBack, vTransform, vTransformBack)
|
||||
|
||||
internal inline fun <E, R> MutableCollection<E>.shadowMap(
|
||||
crossinline transform: (E) -> R,
|
||||
crossinline transformBack: (R) -> E
|
||||
|
@ -17,6 +17,7 @@ import net.mamoe.mirai.console.data.SerializableValue.Companion.serializableValu
|
||||
import net.mamoe.mirai.console.data.SerializerAwareValue
|
||||
import net.mamoe.mirai.console.data.valueFromKType
|
||||
import net.mamoe.mirai.console.internal.command.qualifiedNameOrTip
|
||||
import kotlin.contracts.contract
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.KType
|
||||
|
||||
@ -148,4 +149,17 @@ internal fun KClass<*>.isPrimitiveOrBuiltInSerializableValue(): Boolean {
|
||||
|
||||
@PublishedApi
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
internal inline fun <R> Any.cast(): R = this as R
|
||||
internal inline fun <reified R> Any.cast(): R {
|
||||
contract {
|
||||
returns() implies (this@cast is R)
|
||||
}
|
||||
return this as R
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
internal inline fun <reified R> Any.castOrInternalError(): R {
|
||||
contract {
|
||||
returns() implies (this@castOrInternalError is R)
|
||||
}
|
||||
return (this as? R) ?: error("Internal error: ${this::class} cannot be casted to ${R::class}")
|
||||
}
|
||||
|
@ -42,22 +42,47 @@ internal object BotManagerImpl : BotManager {
|
||||
}
|
||||
}
|
||||
|
||||
internal object ManagersConfig : AutoSavePluginData() {
|
||||
private val managers: MutableMap<Long, MutableSet<Long>> by value()
|
||||
|
||||
internal operator fun get(bot: Bot): MutableSet<Long> = managers.getOrPut(bot.id, ::mutableSetOf)
|
||||
internal object ManagersConfig : AutoSavePluginConfig() {
|
||||
private val managers by value<MutableMap<Long, MutableSet<Long>>>().withEmptyDefault()
|
||||
internal operator fun get(bot: Bot): MutableSet<Long> = managers[bot.id]!!
|
||||
}
|
||||
|
||||
|
||||
internal fun CoroutineContext.overrideWithSupervisorJob(): CoroutineContext = this + SupervisorJob(this[Job])
|
||||
internal fun CoroutineScope.childScope(context: CoroutineContext = EmptyCoroutineContext): CoroutineScope =
|
||||
CoroutineScope(this.coroutineContext.overrideWithSupervisorJob() + context)
|
||||
CoroutineScope(this.childScopeContext(context))
|
||||
|
||||
internal fun CoroutineScope.childScopeContext(context: CoroutineContext = EmptyCoroutineContext): CoroutineContext =
|
||||
this.coroutineContext.overrideWithSupervisorJob() + context
|
||||
|
||||
internal object ConsoleDataScope : CoroutineScope by MiraiConsole.childScope() {
|
||||
private val data: Array<out PluginData> = arrayOf()
|
||||
private val configs: Array<out PluginConfig> = arrayOf(ManagersConfig)
|
||||
|
||||
fun reloadAll() {
|
||||
data.forEach { dt ->
|
||||
ConsoleBuiltInPluginDataStorage.load(ConsoleBuiltInPluginDataHolder, dt)
|
||||
}
|
||||
configs.forEach { config ->
|
||||
ConsoleBuiltInPluginConfigStorage.load(ConsoleBuiltInPluginConfigHolder, config)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal object ConsoleBuiltInPluginDataHolder : AutoSavePluginDataHolder,
|
||||
CoroutineScope by MiraiConsole.childScope() {
|
||||
override val autoSaveIntervalMillis: LongRange = 30.minutesToMillis..60.minutesToMillis
|
||||
CoroutineScope by ConsoleDataScope.childScope() {
|
||||
override val autoSaveIntervalMillis: LongRange = 1.minutesToMillis..10.minutesToMillis
|
||||
override val name: String get() = "ConsoleBuiltIns"
|
||||
}
|
||||
|
||||
internal object ConsoleBuiltInPluginConfigHolder : AutoSavePluginDataHolder,
|
||||
CoroutineScope by ConsoleDataScope.childScope() {
|
||||
override val autoSaveIntervalMillis: LongRange = 1.minutesToMillis..10.minutesToMillis
|
||||
override val name: String get() = "ConsoleBuiltIns"
|
||||
}
|
||||
|
||||
internal object ConsoleBuiltInPluginDataStorage :
|
||||
PluginDataStorage by MiraiConsoleImplementationBridge.dataStorageForBuiltIns
|
||||
PluginDataStorage by MiraiConsoleImplementationBridge.dataStorageForBuiltIns
|
||||
|
||||
internal object ConsoleBuiltInPluginConfigStorage :
|
||||
PluginDataStorage by MiraiConsoleImplementationBridge.configStorageForBuiltIns
|
@ -12,7 +12,7 @@
|
||||
package net.mamoe.mirai.console.plugin.jvm
|
||||
|
||||
import net.mamoe.mirai.console.internal.plugin.JvmPluginInternal
|
||||
import net.mamoe.mirai.utils.minutesToSeconds
|
||||
import net.mamoe.mirai.utils.minutesToMillis
|
||||
import net.mamoe.mirai.utils.secondsToMillis
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
import kotlin.coroutines.EmptyCoroutineContext
|
||||
@ -28,5 +28,5 @@ public abstract class AbstractJvmPlugin @JvmOverloads constructor(
|
||||
) : JvmPlugin, JvmPluginInternal(parentCoroutineContext) {
|
||||
public final override val name: String get() = this.description.name
|
||||
|
||||
public override val autoSaveIntervalMillis: LongRange = 30.secondsToMillis..10.minutesToSeconds
|
||||
public override val autoSaveIntervalMillis: LongRange = 30.secondsToMillis..10.minutesToMillis
|
||||
}
|
@ -48,9 +48,11 @@ fun initTestEnvironment() {
|
||||
override val consoleCommandSender: ConsoleCommandSender = object : ConsoleCommandSender() {
|
||||
override suspend fun sendMessage(message: Message) = println(message)
|
||||
}
|
||||
override val dataStorageForJarPluginLoader: PluginDataStorage get() = MemoryPluginDataStorage()
|
||||
override val configStorageForJarPluginLoader: PluginDataStorage get() = TODO("Not yet implemented")
|
||||
override val dataStorageForBuiltIns: PluginDataStorage get() = MemoryPluginDataStorage()
|
||||
override val dataStorageForJarPluginLoader: PluginDataStorage = MemoryPluginDataStorage()
|
||||
override val configStorageForJarPluginLoader: PluginDataStorage = MemoryPluginDataStorage()
|
||||
override val dataStorageForBuiltIns: PluginDataStorage = MemoryPluginDataStorage()
|
||||
override val configStorageForBuiltIns: PluginDataStorage = MemoryPluginDataStorage()
|
||||
|
||||
override val consoleInput: ConsoleInput = object : ConsoleInput {
|
||||
override suspend fun requestInput(hint: String): String {
|
||||
println(hint)
|
||||
|
@ -10,7 +10,6 @@
|
||||
package net.mamoe.mirai.console.data
|
||||
|
||||
import kotlinx.serialization.json.Json
|
||||
import net.mamoe.mirai.console.data.AutoSavePluginDataHolder.Companion.createPluginData
|
||||
import net.mamoe.mirai.console.plugin.jvm.KotlinPlugin
|
||||
import net.mamoe.mirai.console.util.ConsoleInternalAPI
|
||||
import org.junit.jupiter.api.Test
|
||||
@ -22,7 +21,7 @@ internal class PluginDataTest {
|
||||
|
||||
object MyPlugin : KotlinPlugin()
|
||||
|
||||
class MyPluginData : PluginData by MyPlugin.createPluginData() {
|
||||
class MyPluginData : AutoSavePluginData() {
|
||||
var int by value(1)
|
||||
val map: MutableMap<String, String> by value()
|
||||
val map2: MutableMap<String, MutableMap<String, String>> by value()
|
||||
|
@ -58,7 +58,6 @@ import java.util.*
|
||||
/**
|
||||
* mirai-console-pure 后端实现
|
||||
*
|
||||
* @see MiraiConsoleFrontEndPure 前端实现
|
||||
* @see MiraiConsolePureLoader CLI 入口点
|
||||
*/
|
||||
internal class MiraiConsoleImplementationPure
|
||||
@ -71,7 +70,8 @@ internal class MiraiConsoleImplementationPure
|
||||
override val consoleCommandSender: ConsoleCommandSender = ConsoleCommandSenderImpl,
|
||||
override val dataStorageForJarPluginLoader: PluginDataStorage = MultiFilePluginDataStorage(rootPath.resolve("data")),
|
||||
override val dataStorageForBuiltIns: PluginDataStorage = MultiFilePluginDataStorage(rootPath.resolve("data")),
|
||||
override val configStorageForJarPluginLoader: PluginDataStorage = MultiFilePluginDataStorage(rootPath.resolve("config"))
|
||||
override val configStorageForJarPluginLoader: PluginDataStorage = MultiFilePluginDataStorage(rootPath.resolve("config")),
|
||||
override val configStorageForBuiltIns: PluginDataStorage = MultiFilePluginDataStorage(rootPath.resolve("config"))
|
||||
) : MiraiConsoleImplementation, CoroutineScope by CoroutineScope(SupervisorJob()) {
|
||||
override val mainLogger: MiraiLogger by lazy {
|
||||
MiraiConsole.newLogger("main")
|
||||
|
Loading…
Reference in New Issue
Block a user