Fix delegation in Values;

Rename IllegalMiraiConsoleImplementationError to MalformedMiraiConsoleImplementationError;
Support Managers command
Misc improvements
This commit is contained in:
Him188 2020-08-26 21:27:28 +08:00
parent 71b1464e25
commit a6aa160a97
17 changed files with 331 additions and 108 deletions

View File

@ -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)
}

View File

@ -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()

View File

@ -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}")
}
}
/*
/**

View File

@ -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")

View File

@ -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` 初始化的属性列表.
*

View File

@ -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>
}
}

View File

@ -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) })
)
}
}

View File

@ -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
}
}

View File

@ -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)
}
}

View File

@ -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) {

View File

@ -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

View File

@ -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}")
}

View File

@ -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

View File

@ -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
}

View File

@ -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)

View File

@ -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()

View File

@ -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")